在本作业中,我们将探讨如何使用pthread库提供的条件变量
来实现barrier
。barrier是应用程序中的一个点,在这个点上,所有线程都必须等待,直到所有其他线程也到达该点。条件变量是一种序列协调技术,类似于xv6的sleep和wakeup
。
下载barrier.c,然后在你的手提电脑或Athena machine上编译:
$ gcc -g -O2 -pthread barrier.c
$ ./a.out 2
Assertion failed: (i == t), function thread, file barrier.c, line 55.
2指定在barrier上同步的线程数(barrier.c中的nthread)。每个线程都处于一个紧密的循环中。在每个循环迭代中,一个线程调用barrier(),然后休眠若干微秒。assert触发
,因为一个线程在另一个线程到达barrier之前离开barrier。理想的行为是所有线程都应该阻塞,直到nthread调用barrier
(这里的nthread是不是就是线程号i==nthread的那个线程?错,应该是本轮调用barrier的线程总数bstate.nthread
)。
你的目标是实现这理想的行为。除了您以前见过的锁原语
之外,您还需要以下新的pthread原语
(有关详细信息,请参阅man pthreads):
//函数pthread_cond_wait()使线程阻塞在一个条件变量上
pthread_cond_wait(&cond, &mutex); // go to sleep on cond, releasing lock mutex
pthread_cond_broadcast(&cond); // wake up every thread sleeping on cond
pthread_cond_wait
在被调用时释放
mutex,并在返回前重新获取
mutex。
我们已经给出了barrier_init()。您的工作是实现barrier()
,这样就不会发生恐慌。我们已经为你们定义了struct barrier;它的字段供您使用。
有两个问题会使你的任务复杂化:
bstate.round
记录了当前轮。每个轮开始时你都应该增加bstate.round。static void
barrier()
{
pthread_mutex_lock(&bstate.barrier_mutex);
bstate.nthread++;
printf("in round %d as %d\n ",bstate.round, bstate.nthread);
if(bstate.nthread!=nthread)
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
else{
bstate.round++;
bstate.nthread=0;
//printf("The %d start\n",bstate.round);
//pthread_mutex_unlock(&bstate.barrier_mutex); why can't?
pthread_cond_broadcast(&bstate.barrier_cond);
//pthread_mutex_unlock(&bstate.barrier_mutex); why can't?
}
pthread_mutex_unlock(&bstate.barrier_mutex);
}
使用一个、两个和多个线程测试代码。
//使用三个线程,并自定义输出
...
in round 19996 as 1
in round 19996 as 2
in round 19996 as 3
in round 19997 as 1
in round 19997 as 2
in round 19997 as 3
in round 19998 as 1
in round 19998 as 2
in round 19998 as 3
in round 19999 as 1
in round 19999 as 2
in round 19999 as 3
OK; passed
1.要明白这里的主要任务
是:每一轮bstate.round是要每一个线程都调用一遍barrier,调用之后阻塞在条件变量cond上,当本轮最后那个线程(即n==nthread bstate.nthread==nthread)调用完barrier后,下一轮就要开始了,btate.round++并且所有阻塞在cond上的线程都应该释放。
2.条件变量cond
与互斥锁mutex
在这里各起什么作用?怎么使用?
cond就相当于一个"队列"一样,如果本轮还有线程没调用barrier,那当前线程应该阻塞在cond上
,即进"队列"。当本轮最后那个线程调用了barrier,那意味着下一轮就要开始了,bstate.nthread=0,bstate.round++,然后将所有阻塞在cond中的进程唤醒,即全部出"队列"。
而mutex的使用,主要是为了保护bstate.nthread与bstate.round
,避免多个线程同时修改这两个变量。
3.我代码中,如果pthread_mutex_unlock放在else里面就会发生死锁,为什么?
按道理如果没进else就会在pthread_cond_wait中把mutex释放,除非存在调用pthread_cond_wait失败的情况导致mutex没释放。