posix多线程程序使用条件变量的一个常见bug

    因为类似的问题至少碰到3次以上,虽然很简单但是每次都花了不少时间来定位,所以决定写个demo来演示一下:)程序逻辑比较简单,主线程读入一个整数,分别有两个线程对这个整数求1~n的累加和。代码如下:

 1 #include <iostream>
2 #include <pthread.h>
3 #include <string>
4 #include <unistd.h>
5
6 using namespace std;
7
8 int Number = 0;
9 pthread_mutex_t NMutex;
10 pthread_cond_t NCond;
11
12 void *thread1(void *arg)
13 {
14 pthread_detach(pthread_self());
15 pthread_mutex_lock(&NMutex);
16 while (Number <= 0)//等待主线程读入Number
17 pthread_cond_wait(&NCond, &NMutex);
18 int Count = Number;
19 int Sum = 1;
20 for (int i = 1; i < Count; i++)
21 Sum += i;
22 cout << "count by thread1 is " << Sum << endl;
23 pthread_mutex_unlock(&NMutex);
24 return NULL;
25 }
26
27 void *thread2(void *arg)
28 {
29 pthread_detach(pthread_self());
30 pthread_mutex_lock(&NMutex);
31 while (Number <= 0 )//等待主线程读入Number
32 pthread_cond_wait(&NCond, &NMutex);
33 int Count = Number;
34 int Sum = 1;
35 for (int i = 1; i < Count; i++)
36 Sum += i;
37 cout << "count by thread2 is " << Sum << endl;
38 pthread_mutex_unlock(&NMutex);
39 return NULL;
40 }
41
42 int main(int argc, char* argv[])
43 {
44 pthread_mutex_init(&NMutex, NULL);
45 pthread_cond_init(&NCond, NULL);
46 pthread_t p1, p2;
47 pthread_create(&p1, NULL, thread1, NULL);
48 pthread_create(&p2, NULL, thread2, NULL);
49
50 //begin input
51 pthread_mutex_lock(&NMutex);
52 cout << "input a number ";
53 cin >> Number;
54 pthread_mutex_unlock(&NMutex);
55 pthread_cond_signal(&NCond);
56 //end input
57
58 pthread_exit(NULL);
59 }

    运行结果:
    input a number
    5
    count by thread1 is 11
    count by thread2 is 11

  结果虽然没错,但是却有一个疑惑,主线程里明明是用了pthread_cond_signal,它应该是只唤醒一个线程,但是为什么thread1和thread2都执行了呢。把pthread_cond_signal换成pthread_cond_broadcast运行结果也是一样的。如果把//begin input 到 //end input这一段代码放到另外一个线程去执行,就只有一个输出了,也就是说pthread_cond_signal就只唤醒了一个线程了。为什么会这样呢?难道在主线程调用pthread_cond_signal有什么特殊性吗?

  其实这里的区别并不在于input段代码是在主线程还是在子线程,而是因为thread1和thread2根本没进入wait状态,分析一下程序的执行过程:
  主线程创建p1和p2后三个线程(主线程、p1、p2)的执行顺序是未知的,也就是说不知道哪个会先执行。假设主线程先执行,那么主线程获得NMutex锁,p1和p2都在 pthread_mutex_lock(&NMutex);处阻塞。当主线程读入Number后解锁,这时Number已经不为0了,无论p1还是p2获得锁都不会进入wait状态,所以两个线程都顺利执行完。这就是上面运行结果所表现的情况:调用pthread_cond_signal却唤醒两个线程的原因是跟本就没有线程在wait状态,两个线程都只是带等待解锁,即使不调用pthread_cond_signal,运行结果也是一样。
  上面的分析是基于主线程先执行的情况,如果是子线程先执行,结果就是我们期望的了。但是上面的代码并没有保证哪个线程先执行,把input段放到另一个独立的线程(p3)去执行获得了正确结果也是偶然的,如果p3比p1、p2先执行那么结果还是跟上面一样。在这个例子中,要想获得正确的结果就必须有额外的代码保证主线程和子线程的执行顺序(让子线程先执行)。

http://blog.csdn.net/nightfallrove/article/details/2483403

原文地址:https://www.cnblogs.com/eustoma/p/2415716.html