公认的答案非常狭隘地针对问题中的代码;它无法解释问题的实际根本原因,以及如何自信地确定这一点
班
错误,或者如何一般地修复它们。
从本质上讲,这是一个
data race
程序错误你有一个变量
ThreadArgs myargs = {customer, ordered_product, ordered_quantity};
然后传递这个变量
通过参考
到线程过程。
int rc = pthread_create(&mythreads[i], NULL, thread_function, &myargs);
从您进行此调用的那一刻起,假设调用成功,则有两个线程能够读取
并写入
到变量。你必须以某种方式确保一个线程所做的不会
冲突
[这是一个官方术语]与其他线程的作用。
现在,代码中实际存在的冲突是,父线程
破坏
变量(通过传递到for循环的下一次迭代),而不确保子线程首先从中读取。然而,如果变量仅仅是
覆盖的
在父线程中,例如,如果它的结构是这样的:
// This code is still wrong
ThreadArgs myargs;
for (int i = 0; i < NO_OF_THREADS; ++i) {
myargs.ordered_product = (rand() % NO_OF_PRODUCTS);
myargs.ordered_quantity = (rand() % 10) + 1;
myargs.customer = (rand() % NO_OF_CUSTOMERS);
pthread_create(&mythreads[i], NULL, thread_function, &myargs);
}
有四种通用策略可以修复像这样的错误:
-
如果你只需要通过一个
int
值,例如文件描述符,或任何其他可以强制转换为的值
void *
在不丢失信息的情况下返回,您可以通过值而不是通过引用进行传递:
for (;;) {
int clientfd = accept(listenfd);
if (clientfd >= 0) {
pthread_t t;
int err = pthread_create(&t, create_detached, handle_client,
(void *)(intptr_t)clientfd);
if (err) {
log_error(err);
close(clientfd);
}
}
}
注意你是
不
正在获取的地址
clientfd
在该代码中,您正在转换其
价值
到
无效*
以便满足期望的参数类型。还要注意,父线程
不
关闭clientfd,除非
pthread_create
失败。我们说,子线程“拥有”文件描述符——它负责关闭它。线程过程如下所示:
void *handle_client(void *arg) {
int clientfd = (int)(intptr_t)arg;
// ... communicate with the client ...
close(clientfd);
return 0;
}
-
将要传递给线程的所有数据放在分配的块中
malloc
;将其释放到线程内部。
for (int i = 0; i < NO_OF_THREADS; ++i) {
ThreadArgs *myargs = malloc(sizeof(ThreadArgs));
if (!myargs) {
perror_and_exit("malloc");
}
myargs->ordered_product = (rand() % NO_OF_PRODUCTS);
myargs->ordered_quantity = (rand() % 10) + 1;
myargs->customer = (rand() % NO_OF_CUSTOMERS);
pthread_create(&mythreads[i], NULL, thread_function, myargs);
}
-
如果父线程需要访问传递给每个线程的数据,则在该线程退出后
没有
需要在子线程运行时触摸数据,此时最好像使用pthread_t句柄那样将数据放入数组中:
pthread_t mythreads[NO_OF_THREADS];
ThreadArgs myargs[NO_OF_THREADS];
for (int i = 0; i < NO_OF_THREADS; ++i) {
// initialize ThreadArgs[i] here
pthread_create(&mythreads[i], NULL, thread_function, &myargs[i]);
}
for (int i = 0; i < NO_OF_THREADS; ++i) {
pthread_join(mythreads[i], NULL);
}
// at this point, and ONLY at this point, it is safe
// for the parent thread to look at mythreads[] again
-
最后,如果两个或多个线程都在运行时需要访问一个数据结构——如果无法安排每个数据片段在线程的生命周期内只由一个线程访问——那么您需要使用某种形式的锁定或原子操作。为了完整起见,以下是如何使用
pthread_barrier_t
对象来修复问题中的代码:
// this needs to be a global variable
pthread_barrier_t init_barrier;
// for loop in main:
ThreadArgs myargs;
pthread_barrier_init(&init_barrier, NULL, 2);
for (int i = 0; i < NO_OF_THREADS; i++) {
myargs.ordered_product = (rand() % NO_OF_PRODUCTS);
myargs.ordered_quantity = (rand() % 10) + 1;
myargs.customer = (rand() % NO_OF_CUSTOMERS);
int rc = pthread_create(&mythreads[i], NULL, thread_function, &myargs);
if (rc != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(rc));
exit(1);
}
pthread_barrier_wait(&init_barrier);
}
pthread_barrier_destroy(&init_barrier);
// thread function
void *thread_function(void *arg) {
ThreadArgs myargs = *(ThreadArgs *)arg;
pthread_barrier_wait(&init_barrier);
// can safely access myargs here
}
(给你练习:为什么
不能
你用互斥而不是屏障来解决这个问题的特定实例吗?)