笔记018 - HW9: Barriers

题意

启动多个进程,每个进程执行20000个循环。对于每一个循环周期,确保多个进程都执行到同一个点,然后继续下一次循环。

解决思路

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

可参考的函数使用:

1
2
3
条件变量的使用:
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会先解除之前的pthread_mutex_lock锁定的mutex,然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源。

在barrier()函数中加入互斥锁和条件变量的控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void 
barrier()
{
pthread_mutex_lock(&bstate.barrier_mutex);
bstate.nthread++;
if(bstate.nthread != nthread){
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
pthread_mutex_unlock(&bstate.barrier_mutex);
}else{
pthread_cond_broadcast(&bstate.barrier_cond);
bstate.round++;
bstate.nthread = 0;
pthread_mutex_unlock(&bstate.barrier_mutex);
}
}

bstate.nthread变量的累加用于决定:由最后一个进入barrier()的线程来进行broadcast。注意到pthread_cond_wait函数本身先释放锁、休眠、被唤醒、加锁的流程;执行broadcast之后,该线程会累加周期数bstate.round并清空bstate.nthread,然后释放锁,只有当该线程释放锁,其他线程才真正能在pthread_cond_wait中加锁。

编译并运行:

1
2
3
4
5
6
$ gcc -g -O2 -pthread barrier.c
$ ./a.out 2
```
{% asset_img 1.JPG 编译并运行 %}

# 源代码

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

#include <assert.h>

#include <pthread.h>

// #define SOL

static int nthread = 1;
static int round = 0;

struct barrier {
pthread_mutex_t barrier_mutex;
pthread_cond_t barrier_cond;
int nthread; // Number of threads that have reached this round of the barrier
int round; // Barrier round
} bstate;

static void
barrier_init(void)
{
assert(pthread_mutex_init(&bstate.barrier_mutex, NULL) == 0);
assert(pthread_cond_init(&bstate.barrier_cond, NULL) == 0);
bstate.nthread = 0;
}

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

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);
}
}

int
main(int argc, char argv[])
{
pthread_t
tha;
void *value;
long i;
double t1, t0;

if (argc < 2) {
fprintf(stderr, “%s: %s nthread\n”, argv[0], argv[0]);
exit(-1);
}
nthread = atoi(argv[1]);
tha = malloc(sizeof(pthread_t) * nthread);
srandom(0);

barrier_init();

for(i = 0; i < nthread; i++) {
assert(pthread_create(&tha[i], NULL, thread, (void *) i) == 0);
}
for(i = 0; i < nthread; i++) {
assert(pthread_join(tha[i], &value) == 0);
}
printf(“OK; passed\n”);
}
`

显示 Gitment 评论