条件变量-互斥锁基本使用

背景

芯片a和芯片b串口通信,a发送命令给b,b回复响应给a;b偶尔会主动发送事件消息给a

分析

当b不需要主动发消息给a时,a串口发送,再等消息就行了
现在考虑到b会主动发,需要一个单独的线程,专门用于接收串口消息
发送线程:发命令,等待
接收线程:接收,判断为命令回复,通知到发送线程
需要用到 条件变量和互斥锁
线程间通信,一个线程需要等另一个线程的某个条件满足后,继续运行

具体实现原理还不理解,先用起来,了解下基本用法,见下面例子

例子

先不管串口通信的问题,看一个使用条件变量和互斥锁的示例程序
一个进程,创建2个线程,detach运行,然后主进程sleep
线程a打印,等待 条件变量,等到条件后,再打印
线程b接收键盘输入,当输入为continue,通知线程a继续打印

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

// 全局变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int condition_met = 0;  // 条件标志

// 线程A函数:打印,等待条件变量,然后再打印
void* thread_a(void* arg) {
    pthread_mutex_lock(&mutex);
    
    printf("%s start, then wait......\n", __func__);
    fflush(stdout);
    
    // 等待条件变量
    while (!condition_met) {
        pthread_cond_wait(&cond, &mutex);
    }
    
    printf("%s wait condition met, go on run\n", __func__);
    fflush(stdout);
    
    pthread_mutex_unlock(&mutex);
    return NULL;
}

// 线程B函数:接收键盘输入,当输入为"continue"时通知线程A
void* thread_b(void* arg) {
    char input[100];
    
    printf("线程B:请输入命令(输入'continue'唤醒线程A):\n");
    
    while (1) {
        if (fgets(input, sizeof(input), stdin) != NULL) {
            // 去除换行符
            input[strcspn(input, "\n")] = '\0';
            
            if (strcmp(input, "continue") == 0) {
                pthread_mutex_lock(&mutex);
                condition_met = 1;
                printf("线程B:收到continue命令,唤醒线程A\n");
                pthread_cond_signal(&cond);
                pthread_mutex_unlock(&mutex);
                break;
            } else {
                printf("线程B:无效命令,请输入'continue'\n");
            }
        }
    }
    
    return NULL;
}

int main() {
    pthread_t tid_a, tid_b;
    int ret;
    
    printf("主进程开始\n");
    
    // 创建线程A
    ret = pthread_create(&tid_a, NULL, thread_a, NULL);
    if (ret != 0) {
        fprintf(stderr, "创建线程A失败\n");
        exit(1);
    }
    
    // 创建线程B
    ret = pthread_create(&tid_b, NULL, thread_b, NULL);
    if (ret != 0) {
        fprintf(stderr, "创建线程B失败\n");
        exit(1);
    }
    
    // 分离线程
    pthread_detach(tid_a);
    pthread_detach(tid_b);
    
    printf("主进程:线程已创建并分离,现在sleep 10秒\n");
    
    // 主进程sleep
    sleep(10);
    
    printf("主进程结束\n");
    
    // 清理资源
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    
    return 0;
}

编译运行 ,输出

//编译
gcc -o cond_example test_cond_var_mutex.c -lpthread
//运行
./cond_example
//输出
主进程开始
thread_a start, then wait......
线程B:请输入命令(输入'continue'唤醒线程A):
主进程:线程已创建并分离,现在sleep 10秒
continue
线程B:收到continue命令,唤醒线程A
thread_a wait condition met, go on run
主进程结束
<think>我们正在讨论原子变量和互斥锁使用场景比较。引用内容提到了原子操作和互斥锁在并发编程中的作用。原子操作通常由硬件指令或操作系统提供的原子操作函数实现,保证操作的不可分割性。互斥锁则用于保护临界区,确保同一时间只有一个线程可以执行该段代码。比较点:1.原子变量适用于简单的单个变量操作(如自增、交换等),而互斥锁适用于保护复杂的代码段(临界区)或多个变量的操作。2.性能:原子操作通常比互斥锁更轻量,因为它们在硬件层面实现,不需要线程阻塞和上下文切换。互斥锁在竞争激烈时可能导致线程频繁阻塞和唤醒,开销较大。3.使用场景:原子操作适用于计数器、标志位等简单操作;互斥锁适用于需要保证一系列操作原子性的场景,如对复杂数据结构(链表、树)的修改。下面用代码示例说明:原子变量的使用(以C++11的std::atomic为例):```cpp#include<atomic>std::atomic<int>counter(0);//原子整数voidincrement_counter(){for(inti=0;i<1000;++i){counter.fetch_add(1,std::memory_order_relaxed);//原子自增}}```互斥锁使用(以C++11的std::mutex为例):```cpp#include<mutex>std::mutexmtx;intshared_value=0;voidsafe_increment(){for(inti=0;i<1000;++i){std::lock_guard<std::mutex>lock(mtx);//锁保护开始shared_value++;//非原子操作,但受锁保护}//锁保护结束,自动释放}```在第一个例子中,我们使用原子变量实现计数器,多个线程同时自增不会发生数据竞争。第二个例子使用互斥锁保护一个非原子变量的自增操作,通过锁确保每次自增的原子性。总结:-原子变量:简单、高效,但只能用于基本操作(如load,store,exchange,compare_exchange_weak/strong,fetch_add等)。无法保护多个操作。-互斥锁:通用性强,可以保护任意复杂的临界区,但开销相对较大,且可能导致死锁(需要谨慎设计锁的顺序)。因此,选择使用原子变量还是互斥锁,取决于需要保护的资源的复杂性和性能要求。另外,引用[1]中提到原子操作可以用于实现高级同步机制,比如自旋锁(spinlock)就可以用原子操作实现。而互斥锁(如引用[2]和[3]所述)则是更通用的同步原语,用于解决互斥问题。引用[3]强调了对共享变量的并发操作可能导致的数据不一致问题,而解决这个问题的方法之一就是互斥锁(保证临界区互斥访问)。当然,如果共享变量的操作是原子的(例如原子变量),那么也可以不用互斥锁。因此,在并发编程中:-当共享的操作是简单的单步操作(如赋值、自增等)时,优先使用原子变量。-当共享的操作需要多个步骤(例如,先检查后更新,或者需要更新多个相关变量)时,使用互斥锁。最后,我们生成相关问题。</think> ### 原子变量 vs 互斥锁使用场景比较 #### 一、核心区别对比 | 特性 | 原子变量 | 互斥锁 | |------|----------|--------| | **实现原理** | 硬件级原子指令 (如 x86 的 `LOCK`前缀)[^1] | 操作系统级同步原语 (如 futex)[^2] | | **作用范围** | 单个变量操作 | 代码块保护 | | **阻塞行为** | 非阻塞 | 阻塞等待 | | **性能开销** | 轻量级 (<10ns) | 重量级 (>100ns) | | **适用场景** | 简单数值操作 | 复杂逻辑保护 | #### 二、原子变量优先场景 1. **计数器更新** ```java // Java 示例 AtomicInteger requestCount = new AtomicInteger(0); void handleRequest() { requestCount.incrementAndGet(); // 原子自增 // 处理请求... } ``` > 💡 优势:避免锁竞争导致的吞吐量下降[^1] 2. **标志位切换** ```cpp // C++ 示例 std::atomic<bool> isRunning{false}; void startService() { if(!isRunning.exchange(true)) { // 原子CAS操作 // 启动服务 } } ``` 3. **无锁数据结构** ```c // C 实现无锁栈 void push(Node* new_node) { do { new_node->next = atomic_load(&top); } while(!atomic_compare_exchange_weak(&top, &new_node->next, new_node)); } ``` #### 三、互斥锁优先场景 1. **复合操作保护** ```python # Python 银行转账 lock = threading.Lock() def transfer(acc1, acc2, amount): with lock: # 需要保护两个账户 acc1.balance -= amount acc2.balance += amount ``` > ⚠️ 原子变量无法保证多个变量操作的原子性[^3] 2. **条件同步** ```java // Java 生产者消费者 synchronized(lock) { while(queue.isEmpty()) { lock.wait(); // 需要条件变量配合 } item = queue.poll(); } ``` 3. **复杂对象修改** ```cpp // C++ 哈希表操作 std::mutex mtx; void insert(std::map<K,V>& map, K key, V value) { std::lock_guard<std::mutex> guard(mtx); map[key] = value; // 需保护整个操作链 } ``` #### 四、混合使用策略 1. **锁分解优化** ```java // 分离热点计数器 class Metrics { AtomicInteger successCount = new AtomicInteger(); AtomicInteger failCount = new AtomicInteger(); ReentrantLock dbLock = new ReentrantLock(); void logSuccess() { successCount.incrementAndGet(); // 原子操作 } void writeDb() { dbLock.lock(); // 保护复杂IO // 写入数据库... } } ``` 2. **双重检测锁定** ```c# // C# 单例优化 private static volatile Singleton instance; private static object syncRoot = new Object(); public static Singleton GetInstance() { if (instance == null) { // 原子读 lock (syncRoot) { if (instance == null) instance = new Singleton(); } } return instance; } ``` #### 五、选择决策树 ```mermaid graph TD A[需要同步?] -->|是| B{操作类型} B -->|单个基本类型| C[原子变量] B -->|复合操作| D{竞争程度} D -->|低竞争| E[互斥锁] D -->|高竞争| F[无锁数据结构] F -->|实现复杂| E ``` > 📌 **黄金法则**: > 1. 优先用原子变量处理数值操作 > 2. 互斥锁用于跨操作一致性保障 > 3. 高竞争场景考虑无锁设计[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值