Linux 线程 条件变量

一:条件变量
  直接上最基本的两个函数,先抓主要矛盾:
//等待条件
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);

1:把调用线程放到所等待条件的线程列表上
2:对传进来已经加过锁的互斥量解锁
3:线程进入休眠状态等待被唤醒
注:1、2步为原子操作


//通知条件
int pthread_cond_signal(pthread_cond_t *cond);

1:通知指定条件已经满足
2:等待线程重新锁定互斥锁
3:等待线程需要重新测试条件是否满足



 
二:生产者消费者 
  下面是一个多线程,生产者消费者问题,一个队列放暂存的数据:
 1 #include <iostream>
 2 #include <queue>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <pthread.h>
 6 
 7 using std::cout;
 8 using std::endl;
 9 using std::queue;
10 
11 #define N 100
12 #define ST 10
13 
14 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
15 pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
16 
17 queue<int> que;
18 
19 void* threadProducer(void* arg)
20 {
21     while(true)
22     {
23         sleep(rand() % ST);
24         
25         cout << "Produce try in...
";
26         pthread_mutex_lock(&lock);
27         cout << "Produce in!
";
28         int source = rand() % N;
29         cout << "Produce " << source << endl;
30         que.push(source);
31         pthread_mutex_unlock(&lock);
32         cout << "Produce out
";
33         
34         pthread_cond_signal(&ready);
35     }
36 }
37 
38 void* threadConsumer(void* arg)
39 {
40     while(true)
41     {
42         sleep(rand() % ST);
43         
44         cout << "Consum try in...
";
45         pthread_mutex_lock(&lock);
46         cout << "Consum in!
";
47         while(que.empty())
48         {
49             pthread_cond_wait(&ready, &lock);
50             cout << "Consum from sleep
";
51         }
52         cout << "Consum " << que.front() << endl;
53         que.pop();
54         pthread_mutex_unlock(&lock);
55         cout << "Consum out

";
56     }
57 }
58 
59 int main(void)
60 {
61     pthread_t tProducer, tConsumer;    
62     pthread_create(&tProducer, NULL, threadProducer, NULL);
63     pthread_create(&tConsumer, NULL, threadConsumer, NULL);
64     
65     pthread_join(tProducer, NULL);
66     pthread_join(tConsumer, NULL);
67 
68     exit(0);
69 }
生消

看到倒数的三四行,消费者进去了,发现没有数据了,则睡眠了,然后生产者进去生产了。



 
三:打印的例子
  下面是一个多线程的小例子,线程1打印非3的倍数,线程2打印3的倍数:
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

using std::cout;
using std::endl;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ready = PTHREAD_COND_INITIALIZER;

int data = 0;

void* threadProducer(void* arg)
{
    int i;
    for(i = 1; i < 22; i++)
    {    
        sleep(1);
    
        if(i % 3 != 0)
        {
            cout << "thread1:" << i << endl;
        }
        else
        {
            pthread_mutex_lock(&lock);
            data = i;
            pthread_mutex_unlock(&lock);
            
            pthread_cond_signal(&ready);
        }    
    }
}

void* threadConsumer(void* arg)
{
    while(true)
    {
        pthread_mutex_lock(&lock);
        while(data == 0)    //no data
            pthread_cond_wait(&ready, &lock);
        cout <<"thread2:" << data << endl;
        if(data == 21)
            break;
        else
            data = 0;    //empty data
        pthread_mutex_unlock(&lock);
    }
}

int main(void)
{
    pthread_t tProducer, tConsumer;    
    pthread_create(&tProducer, NULL, threadProducer, NULL);
    pthread_create(&tConsumer, NULL, threadConsumer, NULL);
    
    pthread_join(tProducer, NULL);
    pthread_join(tConsumer, NULL);

    exit(0);
}
3打印

  程序大致这样:线程1中的循环,如果i不是3的倍数就自己打印了,如果是的话,把这个数放到一个地方(由于这个地方可以被线程2发现,所以要加锁访问),然后唤醒等待数据的线程2(如果线程2还没有在等待,那么这个唤醒则丢失,这是个bug,见下),线程2被唤醒后,消费了这个3的倍数,清空数据区。

  上面提到,如果唤醒线程2的消息没有被收到,则bug。看下面的代码,也就多了38一行,让线程2睡了一会,就在它睡觉的那么一会,线程1把3的倍数往那里一扔就走了,自己再继续下两个不是3倍数的数字,这就直接输出了下面两个数字,又到了3倍数,又扔过去覆盖了之前数字:
 1 #include <iostream>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <pthread.h>
 5 
 6 using std::cout;
 7 using std::endl;
 8 
 9 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
10 pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
11 
12 int data = 0;
13 
14 void* threadProducer(void* arg)
15 {
16     int i;
17     for(i = 1; i < 22; i++)
18     {    
19         sleep(1);
20     
21         if(i % 3 != 0)
22         {
23             cout << "thread1:" << i << endl;
24         }
25         else
26         {
27             pthread_mutex_lock(&lock);
28             data = i;
29             pthread_mutex_unlock(&lock);
30             
31             pthread_cond_signal(&ready);
32         }    
33     }
34 }
35 
36 void* threadConsumer(void* arg)
37 {
38     sleep(20);
39     while(true)
40     {
41         pthread_mutex_lock(&lock);
42         while(data == 0)    //no data
43             pthread_cond_wait(&ready, &lock);
44         cout <<"thread2:" << data << endl;
45         if(data == 21)
46             break;
47         else
48             data = 0;    //empty data
49         pthread_mutex_unlock(&lock);
50     }
51 }
52 
53 int main(void)
54 {
55     pthread_t tProducer, tConsumer;    
56     pthread_create(&tProducer, NULL, threadProducer, NULL);
57     pthread_create(&tConsumer, NULL, threadConsumer, NULL);
58     
59     pthread_join(tProducer, NULL);
60     pthread_join(tConsumer, NULL);
61 
62     exit(0);
63 }
bug




 
四:总结  
  从上面可以总结出下面的条件变量的生产者消费者代码模型:
//下面是生产者

pthread_mutex_lock(&lock);    //加锁访问临界区
/*在这里生产数据*/
pthread_mutex_unlock(&lock);    //解锁
    
pthread_cond_signal(&ready);    //通知消费者


//下面是消费者

pthread_mutex_lock(&lock);    //加锁访问临界区
while(没有待消费数据)
        pthread_cond_wait(&ready, &lock);    //睡在这里,等待被唤醒
/*被叫醒了,在这里消费数据*/
pthread_mutex_unlock(&lock);    //解锁
原文地址:https://www.cnblogs.com/jiayith/p/3876624.html