线程同步(条件变量)

线程同步(条件变量)

条件变量是线程的另一种同步机制。
条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
条件本身是由互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量。其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件。

使用条件变量前,必须先对它进行初始化。条件变量的数据类型为pthread_cond_t。它有两种初始化方式:
1、直接把常量PTHREAD_COND_INITIALIZER赋值给静态分配的条件变量。
2、如果条件变量是动态分配的,需要使用pthread_cond_init函数对它进行初始化。

在释放条件变量底层的内存空间前,可以使用pthread_cond_destroy函数对条件变量进行销毁。
注意:不能用多个线程初始化同一个条件变量,当一个线程要使用条件变量的时候确保它是未被使用的。

#include 
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
//两个函数的返回值:若成功,返回0;否则,返回错误编码

除非需要创建一个具有非默认属性的条件变量,否则pthread_cond_init函数的attr参数可以设置为 NULL。

我们使用pthread_cond_wait等待条件变量变为真。如果在给定的时间内条件不能满足,那么会生成一个返回错误码的变量。


#include 
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
//两个函数的返回值:若成功,返回0;否则,返回错误编码

传递给pthread_cond_wait的互斥量对条件进行保护。调用者把锁住的互斥量传给函数,函数然后自动把调用线程放到等待条件的线程列表上,对互斥量解锁。
这就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。
pthread_cond_timedwait函数的功能与pthread_cond_wait函数相似,只是多了一个超时( tsptr)。超时值指定了我们愿意等待多长时间,它是通过timespec结构指定的。

使用方式:

使用pthread_cond_wait方式如下:

pthread _mutex_lock(&mutex)
while或if(线程执行的条件是否成立)
pthread_cond_wait(&cond, &mutex);
/* 线程执行语句 */
pthread_mutex_unlock(&mutex);

这里在调用pthread_cond_wait前需要互斥量进行加锁的原因是:确保在公有资源条件状态改变时,等待条件的线程已经被放到等待条件的线程列表上。

使用while和if判断线程执行条件是否成立的区别:
一般来说,在多线程资源竞争的时候,在一个使用资源的线程里面(消费者)判断资源是否可用,不可用便调用pthread_cond_wait,在另一个线程里面(生产者)如果判断资源可用的话,则调用pthread_cond_signal发送一个资源可用信号,这时候pthread_cond_wait就会返回,并且会再自动对互斥量进行加锁,然后线程接着执行下去。

下面是流程:

等待线程:

pthread_cond_wait前要先加锁
pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
pthread_cond_wait被激活后会再自动加锁

激活线程:


加锁(和等待线程用同一个锁)
pthread_cond_signal发送信号(阶跃信号前最好判断有无等待线程)
解锁

激活线程的三个操作执行时,等待线程都处于pthread_cond_wait中,等待pthread_cond_signal发送信号激活。


实例:

#include 

struct msge {
    struct msge *m_next;
    /* ... more stuff here ...*/
};

struct msge *workq;

pthread_cond_t qready = PTHREAD_COND_INITIALIZER;

pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

//等待条件(消费者)线程1
void process_msg(void){
    struct msge *mp;
    for (;;) {
        pthread_mutex_lock(&qlock);
        while (workq == NULL) {
            /*
            1、等待条件满足
            2、对互斥量进行解锁
            */
            //调用pthread_cond_signal通知条件满足时返回,返回后再次对互斥量加锁
            pthread_cond_wait(&qready, &qlock);
        }
    mp = workq;
    workq = mp->m_next;
    pthread_mutex_unlock(&qlock);
    /* now process the message mp */
    }
}


//产生条件(生产者)线程2
void enqueue_msg(struct msge *mp){
    pthread_mutex_lock(&qlock);
    mp->m_next = workq;
    workq = mp;
    pthread_mutex_unlock(&qlock);
    pthread_cond_signal(&qready);
}

这里用两个线程分别模拟生产者与消费者,消费者线程等待生产者线程产生特定的条件,然后再往下执行,这里因为是两个线程,所以生产者如果先于消费者获得互斥量并调用了pthread_cond_signal,消费者因为还没有进入等待条件状态,所以生产者发送的信号,消费者会忽略掉。

你可能感兴趣的:(Linux程序设计)