MIT6.828学习之homework9:Barriers

在本作业中,我们将探讨如何使用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;它的字段供您使用。

有两个问题会使你的任务复杂化:

  • 你必须处理一连串的barrier calls,每一连串我们都称之为一轮(这里的一轮是指所有的线程都调用一次barrier?对)。bstate.round记录了当前轮。每个轮开始时你都应该增加bstate.round。
  • 您必须处理这样一种情况:一个线程在其他线程退出barrier之前绕着循环运行。特别是,你正在重新使用bstate.nthread从一轮到下一轮。确保离开barrier并绕循环运行的线程不会增加bstate.nthread,当上一轮仍然在使用它。
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没释放。

你可能感兴趣的:(MIT6.828操作系统学习,MIT6.828,homework9,barriers,pthread_mutex_t,pthread_cond_t)