Linux - 线程同步

条件变量

使用互斥锁可以解决线程安全的问题,保证多线程下临界资源数据的正确性.
但是仅仅互斥还是会存在一些问题.
  • 某个线程获取锁之后, 发现数据没有就绪, 又立刻释放锁.
  • 如果这个线程的优先级很高, 那么就可能在释放了锁之后又立刻尝试获取锁, 再立刻释放.
  • 依次类推. 这样虽然并没有发生死锁, 但是这个线程空转又占用了锁资源, 导致其他线程很难获取到这个锁.

条件变量:
  • 当一个线程互斥的访问某个变量时,在某一线程的状态没有发生改变之前,他什么也是做不了.
以打球举例:A负责传球,B负责投篮,当B想要投篮的时候哦,但是发现A并没就将球传给自己之前,就不能去投篮,只能等待A将球传给自己才可以继续执行自己的投篮任务

条件变量函数

初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); 

参数: 
    cond:要初始化的条件变量 
    attr:NULL

销毁
int pthread_cond_destroy(pthread_cond_t *cond);

等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 

参数:    
    cond:要在这个条件变量上等待 
    mutex:互斥量

唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond); 
int pthread_cond_signal(pthread_cond_t *cond);

代码举例:(以打球为例)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_cond_t g_shot;
pthread_mutex_t g_lock;

void* Pass(void* arg)
{
    (void)arg;
    while(1)
    {
        printf("1 : 传球\n");
        usleep(789123);
        pthread_cond_signal(&g_shot);
    }
    return NULL;
}

void* Shot(void* arg)
{
    (void)arg;
    while(1)
    {
        pthread_cond_wait(&g_shot,&g_lock);
        printf("2 : 投篮\n");
        usleep(123456);
    }
    return NULL;
}

int main()
{
    pthread_mutex_init(&g_lock,NULL);
    pthread_cond_init(&g_shot,NULL);

    //1.球员1负责传球
    //2.球员2负责投篮
    pthread_t t1,t2;
    pthread_create(&t1,NULL,Pass,NULL);
    pthread_create(&t2,NULL,Shot,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);

    pthread_mutex_destroy(&g_lock);
    pthread_cond_destroy(&g_shot);
    return 0;
}

未加条件变量的运行结果:

因为我们设置的是传球比较慢一些,投篮比较快一些,可以看到,在"投篮"一直在运行,消耗着资源

加了条件变量的运行结果:
(忽略刚开始的不稳定情况)
我们可以看到,传球和投篮一定是交替运行的,在传球结束以后会发送一个信号,"投篮"发现条件满足了就会开始投篮,在这之前一直等待

为什么 pthread_cond_wait 需要互斥量?
条件变量是为了解决某些情况下互斥锁低效的问题. 因此对条件变量的操作, 必然要和互斥锁密切相关. 
  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。

观察一下的代码看看有什么问题?
pthread_mutex_lock(&mutex);
while(条件为假){
    pthread_mutex_unlock(&mutex);
    
    //  在这一部分可能会出现问题!!!!
    
    pthread_cond_wait(&cond);
    pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);

我们分析以上的代码,可能会出现如下问题:

  • 由于解锁unlock并不是原子操作,所以在线程1调用解锁以后,wait之前,如果线程2获得了互斥量,摒弃了条件满足,发送了信号,那么线程1的wait将错过这个信号,可能导致线程永远阻塞在wait这里.所以,解锁和等待必须是一个原子操作
  • wait函数中的互斥量进入函数以后,回去查看当前的条件是不是为假,如果是假,就将互斥量置为1,知道 wait 返回,把条件改为1,将互斥量置为原来的样子,保证wait不错过信号量,避免阻塞.

条件变量使用规范

  • 等待条件代码
pthread_mutex_lock(&mutex);
while(条件为假)
    pthread_cond_wait(cond,mutex);
修改条件
pthread_mutex_unlock(&mutex);

  •  给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值