一、介绍


本作业要求使用 Pthread 库提供的条件变量,实现 barrier。对于用户级线程来说,一个线程执行到 barrier 后必须停下等待,知道所有线程都到达 barrier。条件变量(condition variable)机制与 xv6 中的 sleep/wakeup 机制类似。

sleep(channel, lock) <-> pthread_cond_wait(cond, mutex)
wakeup(channel)      <-> pthread_cond_broadcast(cond)

下载源文件 barrier.c

编译运行:

wu@wu-insparition:~/Desktop$ gcc -g -O2 -pthread barrier.c 
wu@wu-insparition:~/Desktop$ ./a.out 2
a.out: barrier.c:42: thread: Assertion `i == t' failed.
Aborted (core dumped)

 

二、实现


The 2 specifies the number of threads that synchronize on the barrier

首先分析一下为什么会 failed。

static void *
thread(void *xa)
{
  long n = (long) xa;
  long delay;
  int i;
  for (i = 0; i < 20000; i++) {
    int t = bstate.round;
    assert (i == t);
    barrier();
    usleep(random() % 100);
  }
}

如果有两个线程 A 和 B,线程 A 执行完 barrier() 后,全部变量 bstate.round 值为 1。此时线程 B 进入 for 循环。对于线程 B,全局变量 bstate.round 值为 1,局部变量 i 为 0,执行 assert (i == t) 自然就报错了。

这种两个线程先后通过 barrier() 的做法,也不符合 barrier 的概念。

然而线程们在 barrier 处 block 还不够。最终要实现的是,所有 block 的线程只有一个去执行 barrier() 函数。这样每次循环 bstate.round 的值才能与当前的 i 相等。

static void
barrier()
{
  pthread_mutex_lock(&bstate.barrier_mutex);
  if (++bstate.nthread == nthread)
  {
    pthread_cond_broadcast(&bstate.barrier_cond);
    bstate.round++;
    bstate.nthread = 0;
  }
  else
  {
    pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
  }
  pthread_mutex_unlock(&bstate.barrier_mutex);
}

这样就保证了最后到达的线程将前面所有在 barrier 处 block 线程唤醒,并保证 bstate.round++ 只执行一次。