#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
int done = 0;
void *child(void *arg) {
sleep(1);
printf("\n--- running child thread ---\n\n");
sleep(1);
pthread_mutex_lock(&m);
done = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
return NULL;
int main() {
pthread_t p;
printf("main thread start ...\n");
assert(pthread_create(&p, NULL, child, NULL) == 0);
pthread_mutex_lock(&m);
while (done == 0) {
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
printf("main thread end ...\n");
加锁的地方有两个, 分别是pthread_cond_wait和pthread_cond_signal函数的前后.
锁的意义在于保护, 在临界区加锁, 这样其他线程在执行临界区, 检测到锁被锁住了, 就不能访问执行这段临界区了, 直到锁被释放.
一个最简单的并发问题是如果创建2个线程同时大量操作同一个数自增(比如自增10000000次, 7个0), 那么执行完之后, 这个数往往并没有自增20000000次, 而是更少. 这就是因为在这段临界区中, 由于线程调度的存在, 且自增操作不是原子操作, 而导致某些时刻两个线程"同时"自增而这个数只增加了1. 而在临界区加锁就可以很好地避免这个问题, 执行临界区代码也就是自增时, 它被看作是原子的, 因而每执行一次就增一次.
当然, 加锁保证了正确性, 但会影响性能(甚至比一个线程自增20000000还要慢). 这是因为在加了锁的临界区, 另一个线程运行到这里而检测到这个临界区被锁住, 他就会退出, 继续进入就绪状态等待被调度. 在这种高并发的前提下, 这样的冲突是会大量存在的, 而线程的上下文切换会消耗大量的时间, 因而反而比只有一个线程运行要更慢了.
好了, 说了这么多关于锁的题外话, 回到当前的问题. 锁在这个条件变量这里起到了什么做用?
我们反向地思考. 思考如果没有锁, 会有哪些特殊情况发生:
假设线程执行到了while (done == 0) , 此时新线程没有执行, 因而条件判断为真. 接下来应该执行pthread_cond_wait(&c, &m);. 然而, 此时发生时钟中断, 内核调度程序, 新线程抢占执行, 并且执行完了, done 变成1了, 而且还给条件变量发送了signal了.
然后回到主线程主线程继续执行pthread_cond_wait(&c, &m);, 然后凉了. 因为主线程会挂起, 等待信号被发送给条件变量从而将它唤醒. 但是, 新线程已经执行完了, 它无法被唤醒了.
这之中的主要矛盾就是, while (done == 0)和pthread_cond_wait(&c, &m);被分开了. 他们本来应该是在一起的. pthread_cond_wait(&c, &m);是在done == 0为真的条件下执行的, 但当新线程执行完毕, done == 0不为真了, 他就不应该执行了.
所以这里是临界区, 它缺把锁. 将while (done == 0) { pthread_cond_wait(&c, &m); } 锁住了, 在执行pthread_cond_wait(&c, &m);之前CPU去执行新线程了, 但他检测到锁m被锁住了, 就放弃执行, 从而不会提前发送signal.
所以, 在并发情况下, 互斥锁在这里尤其重要.
一个线程的结束, 有两种方式, 一种是正常执行完毕, 函数return之后线程结束.
第二种是调用pthread_exit函数, 它的原型为

简单来说我们可以在线程中使用这样的方法pthread_exit(NULL);去终止线程.
下面是一个小demo
* 基本的多线程程序,有join和无join
#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
void *child(void *arg) {
sleep(1);
printf("\n--- running child thread ---\n\n");
pthread_exit(NULL);
printf("do I print?\n");
sleep(1);
int main() {
pthread_t p;
printf("main thread start ...\n");
assert(pthread_create(&p, NULL, &child, NULL) == 0);
pthread_join(p, NULL);
printf("main thread end ...\n");
do I print? 这句话是不会打印出来的, 因为在此之前, 这个线程已经提前终止了.
参考资料: