条件变量是另一种常用的变量。它也常常被保存为全局变量,并和互斥锁合作。
假设这样一个状况: 有100个工人,每人负责装修一个房间。当有10个房间装修完成的时候,老板就通知相应的十个工人一起去喝啤酒。
我们如何实现呢?老板让工人在装修好房间之后,去检查已经装修好的房间数。但多线程条件下,会有竞争条件的危险。也就是说,其他工人有可能会在该工人装修好房子和检查之间完成工作。采用下面方式解决:
/*mu: global mutex, cond: global codition variable, num: global int*/ mutex_lock(mu) num = num + 1; /*worker build the room*/ if (num <= 10) { /*worker is within the first 10 to finish*/ cond_wait(mu, cond); /*wait*/ printf("drink beer"); } else if (num = 11) { /*workder is the 11th to finish*/ cond_broadcast(mu, cond); /*inform the other 9 to wake up*/ } mutex_unlock(mu);
上面使用了条件变量。条件变量除了要和互斥锁配合之外,还需要和另一个全局变量配合(这里的num, 也就是装修好的房间数)。这个全局变量用来构成各个条件。
具体思路如下。我们让工人在装修好房间(num = num + 1)之后,去检查已经装修好的房间数( num < 10 )。由于mu被锁上,所以不会有其他工人在此期间装修房间(改变num的值)。如果该工人是前十个完成的人,那么我们就调用cond_wait()函数。
cond_wait()做两件事情,一个是释放mu,从而让别的工人可以建房。另一个是等待,直到cond的通知。这样的话,符合条件的线程就开始等待。
当有通知(第十个房间已经修建好)到达的时候,condwait()会再次锁上mu。线程的恢复运行,执行下一句prinft("drink beer") (喝啤酒!)。从这里开始,直到mutex_unlock(),就构成了另一个互斥锁结构。
那么,前面十个调用cond_wait()的线程如何得到的通知呢?我们注意到elif if,即修建好第11个房间的人,负责调用cond_broadcast()。这个函数会给所有调用cond_wait()的线程放送通知,以便让那些线程恢复运行。
条件变量特别适用于多个线程等待某个条件的发生。如果不使用条件变量,那么每个线程就需要不断尝试获得互斥锁并检查条件是否发生,这样大大浪费了系统的资源。