我正在按照 pthread 教程中的 pthread 教程进一步研究 pthread 障碍 - Peter Chapin,3.2 Barriers pg 11 在线程函数中使用两个障碍,第一个暂停所有线程,直到所有线程都达到
loop_barrier
在 PTHREAD_BARRIER_SERIAL_THREAD
的屏障返回和 prep_barrier
的线程函数中的后续屏障之后,被选择执行任何串行清理的任意线程确认,这确保所有线程都被暂停,直到串行清理完成。
我的理解是,这允许线程连续运行,同时在处理中的给定指针处提供线程同步,其中在所有线程继续以并发方式继续运行之前完成每个周期的所有工作。该示例简单地显示了一个这样的循环发生了什么,然后设置了一个done
标志并且线程函数返回。所有线程都挂起并等待
loop_barrier
和
prep_barrier
,但问题是以下线程函数返回程序在第一个
pthread_join()
上停止,
gdb
解释说,相当无益,是
"in pthread_barrier_destroy () from /lib64/libpthread.so.0"
的结果
本教程仅提供了线程函数和主程序的框架,我只是提供了完成它的最低限度,声明了一个结构来保存每个线程函数中for
循环的不同循环限制和成员来保存线程
for
循环变量值的索引和总和。显然我并不像我认为的那样完全理解这些障碍。导致连接挂起的代码是:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define NCPU 4
#define ITER_PER_CPU 100
typedef struct {
int index, start, end;
unsigned sum;
} loop_data;
pthread_barrier_t loop_barrier; /* global barriers (could pass in data) */
pthread_barrier_t prep_barrier;
void *thread_fn (void *data)
{
int done = 0,
i = 0;
loop_data *thread_data = data;
do {
for (i = thread_data->start; i < thread_data->end; i++) {
/* each arg gets separate loop_data - do work */
thread_data->sum += i;
}
/* suspend on barrier and do any per-cycle cleanup */
if (pthread_barrier_wait (&loop_barrier) == PTHREAD_BARRIER_SERIAL_THREAD) {
puts ("PTHREAD_BARRIER_SERIAL_THREAD");
/* no actual per-cycle cleanup, just set done flag */
done = 1;
}
/* suspend on barrier until per-cycle cleanup complete */
pthread_barrier_wait (&prep_barrier);
printf ("thread index: %d, sum: %d\n",
thread_data->index, thread_data->sum);
} while (!done);
return data;
}
int main (void) {
pthread_t id[NCPU];
pthread_attr_t attr;
loop_data arr[NCPU] = {{ .start = 0 }};
void *res;
int rtn = 0;
/* initialize barriers and validate */
if ((rtn = pthread_barrier_init (&loop_barrier, NULL, NCPU))) {
handle_error_en (rtn, "pthread_barrier_init-loop_barrier");
}
if ((rtn = pthread_barrier_init (&prep_barrier, NULL, NCPU))) {
handle_error_en (rtn, "pthread_barrier_init-prep_barrier");
}
/* initialize thread attributes (using defaults) and validate */
if ((rtn = pthread_attr_init (&attr))) {
handle_error_en (rtn, "pthread_attr_init");
}
/* set data index, start, end and create/validate each thread */
for (int i = 0; i < NCPU; i++) {
/* initialize index, start / end values */
arr[i].index = i;
arr[i].start = i * ITER_PER_CPU;
arr[i].end = (i + 1) * ITER_PER_CPU;
printf ("id: %d, start: %3d, end: %3d\n", i, arr[i].start, arr[i].end);
/* create thread and validate */
if ((rtn = pthread_create (&id[i], &attr, thread_fn, &arr[i]))) {
handle_error_en (rtn, "pthread_create");
}
}
/* join all threads and compare sums from threads with sums in main */
for (int i = 0; i < NCPU; i++) {
loop_data *data = NULL;
/* join and validate */
printf ("joining thread index: %d\n", i);
if ((rtn = pthread_join (id[i], &res))) {
fprintf (stderr, "error: thread %d\n", i);
handle_error_en (rtn, "pthread_join");
}
data = res; /* pointer to return struct provided through parameter */
printf ("thread index: %d joined\n", data->index);
}
/* destroy barriers and validate */
if ((rtn = pthread_barrier_destroy (&loop_barrier))) {
handle_error_en (rtn, "pthread_barrier_destroy-loop_barrier");
}
if ((rtn = pthread_barrier_destroy (&prep_barrier))) {
handle_error_en (rtn, "pthread_barrier_destroy-prep_barrier");
}
}
示例使用/输出
$ ./bin/pthread-vtctut-04
id: 0, start: 0, end: 100
id: 1, start: 100, end: 200
id: 2, start: 200, end: 300
id: 3, start: 300, end: 400
joining thread index: 0
PTHREAD_BARRIER_SERIAL_THREAD
thread index: 2, sum: 24950
thread index: 0, sum: 4950
thread index: 1, sum: 14950
thread index: 3, sum: 34950
^C
在代码挂在 94
处
if ((rtn = pthread_join (id[i], &res))) {
处提供的手动中断。那么,为什么每个线程函数都被第二个屏障释放(如
"thread index: x, sum: yyyy"
输出所示,代码在
pthread_join()
中的
main()
上挂起?
这只是部分答案因为我不确定解决问题的正确方法是什么]
发生的事情是对done
的更改没有被其他线程接收,因为您没有memory barrier,所以除了一个线程之外的所有线程都重新启动循环并被阻塞在
pthread_barrier_wait( &loop_barrier )
.
pthread_barrier_init
。这意味着当您再次使用同一个屏障时,将再次是 4 个线程必须到达屏障。但是,只有3个线程会到达循环的第二次迭代,因为获得
PTHREAD_BARRIER_SERIAL_THREAD
的线程将退出循环并终止。这意味着 3 个线程将在第二次循环迭代中卡在
pthread_barrier_wait (&loop_barrier)
上,因为终止的线程将永远不会第二次到达该障碍。因此,函数
main
只能与 4 个线程中的 1 个连接。
PTHREAD_BARRIER_SERIAL_THREAD
的线程没有有效地向其他线程发出停止信号。它设置变量
done
,但这是线程函数的局部变量。该函数的每次执行都有自己的,因此一个线程修改其
done
对于其他线程通过其
done
s是不可见的。由于只有一个线程停止,main 将在第一次或第二次
pthread_join()
调用时阻塞,具体取决于它恰好终止的线程。如果它是挂起的第一个
pthread_join()
调用,那么一个线程将被终止但未加入。无论哪种方式,三个线程都会循环回到第一个屏障并在那里阻塞。通过拉动它来修复它给
done
静态而不是自动存储持续时间,以便它在线程之间共享。您可以通过在函数内使用
static
关键字声明它或将其从函数中拉出到文件范围来实现。如果您希望能够从运行
done
的线程范围之外设置或重置
thread_fn()
,那么后者是唯一可行的选择。