Linux 多线程条件变量同步

条件变量是线程同步的另一种方式,实际上,条件变量是信号量的底层实现,这也就意味着,使用条件变量可以拥有更大的自由度,同时也就需要更加小心的进行同步操作。条件变量使用的条件本身是需要使用互斥量进行保护的,线程在改变条件状态之前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定之后才能计算条件。

模型 #include<pthread.h> pthread_t cond //准备条件变量 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化静态的条件变量 pthread_cond_init() //初始化一个动态的条件变量 pthread_cond_wait() //等待条件变量变为真 pthread_cond_timedwait() //等待条件变量变为真,等待有时间限制。 pthread_cond_signal() //至少唤醒一个等待该条件的线程 pthread_cond_broadcast() //唤醒等待该条件的所有线程 pthread_cond_destroy() //销毁一个条件变量 pthread_cond_init() //初始化一个动态的条件变量 //成功返回0,失败返回error number int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

cond:条件变量指针,这里使用了restrict关键字
attr:条件变量属性指针,默认属性赋NULL

pthread_cond_wait() / pthread_cond_timedwait() //等待条件变量为真。收到pthread_cond_broadcast()或pthread_cond_signal()就唤醒 //成功返回0,失败返回error number 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 abstime);

条件变量的使在多线程的程序中,因为各个线程都可以访问大部分的进程资源,所以我们为了保证公有资源的使用是可以控制的,在一个线程开始使用公有资源之前要尝试获取互斥锁,在使用完毕之后要释放互斥锁,这样就能保证每个公共资源在一个时刻都只能被一个线程使用,在一定程度上得到了控制,但这并不能解决同步的问题,考虑如下两个线程:

Q=create_queue(); pthread_t mutex //线程A,入队 while(1){ lock(mutex); in_queue(Q); unlock(mutex); } //线程B,出队 while(1){ lock(mutex); out_queue(Q); unlock(mutex); }

上述代码可以实现两个线程的互斥,即同一时刻只有一个线程在使用公有资源-队列。但如果线程B获取了锁,但队列中是空的,它的out_queue()也就是没有意义的,所以我们这里更需要一种方法将两个线程进行同步:只有当队列中有数据的时候才进行出队。
我们设计这样一种逻辑:

//线程B,出队 while(1){ lock(mutex); if(队列是空,线程不应该执行){ 释放锁; continue; } out_queue(Q); unlock(mutex); }

这个程序就解决了上述的问题,即便线程B抢到了互斥锁,但是如果队列是空的,他就释放锁让两个线程重新抢锁,希望这次线程A能抢到并往里放一个数据。
但这个逻辑还有一个问题,就是多线程并发的问题,很有可能发生的一种情况是:线程B抢到了锁,发现没有数据,释放锁->线程A立即抢到了锁并往里放了一个数据->线程B执行continue,显然,这种情况下是不应该continue的,因为线程B想要的条件在释放锁之后立即就被满足了,它错过了条件。
So,我们想一种反过来的逻辑:

//线程B,出队 while(1){ lock(mutex); if(队列是空,线程不应该执行){ continue; 释放锁; } out_queue(Q); unlock(mutex); }

显然这种方法有个致命的问题:一旦continue了,线程B自己获得锁就没有被释放,这样线程A不可能抢到锁,而B继续加锁就会形成死锁!
Finaly,我们希望看到一个函数fcn,如果条件不满足,能同时释放锁+停止执行线程,如果条件满足,自己当时获得的锁还在

//线程B,出队 while(1){ lock(mutex); fcn(当前线程不应该执行,mutex) //if(当前线程不应该执行){释放锁“同时” continue;} out_queue(Q); unlock(mutex); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/14799.html