为什么竞争条件会在循环中发生?
Why race condition happens in loop?
我有一个代码可以模拟串行与并行计算。但是,它似乎存在竞争条件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *foo(void *para) {
printf("This is Thread %d running...\n", *(size_t *)para);
fflush(stdout);
sleep(2);
}
int main(void)
{
/* loop version
for (size_t i = 0; i < 4; i++) {
foo((void *)&i);
}
*/
pthread_t pool[4];
for (size_t i = 0; i < 4; i++) {
pthread_create(&pool[i], NULL, foo, (void *)&i);
}
for (size_t i = 0; i < 4; i++) {
pthread_join(pool[i], NULL);
}
return 0;
}
输出:
[william@Notebook Downloads]$ time ./a.out
This is Thread 1 running...
This is Thread 2 running...
This is Thread 4 running...
This is Thread 3 running...
real 0m2.003s
user 0m0.003s
sys 0m0.000s
[william@Notebook Downloads]$ time ./a.out
This is Thread 3 running...
This is Thread 3 running...
This is Thread 2 running...
This is Thread 4 running...
real 0m2.003s
user 0m0.003s
sys 0m0.000s
发生竞争条件的大多数示例是当您有多个线程尝试写入共享值时。但是,这段代码中根本没有写操作。那么,为什么会这样呢?是不是for循环没有按顺序执行?
我什至没有读你的问题。我刚刚查看了您的代码并观察到一个巨大的问题,这可能是您所问问题的根本原因。看下面的循环代码:
pthread_t pool[4];
for (size_t i = 1; i <= 4; i++) {
pthread_create(&pool[i], NULL, foo, (void *)&i);
}
您有一个大小为 4 的 "array",但您正在遍历索引 [1..4]
。 C 和 C++ 数组从索引 0 开始,到 (len-1) 结束。换句话说,您应该遍历索引 [0..3]。向 pool[4]
中插入任何内容都会产生未定义的行为。
代替这一行:
for (size_t i = 1; i <= 4; i++) {
你想要这个:
for (size_t i = 0; i < 4; i++) {
对执行 pthread_join
语句的第二个循环应用相同的修复。
同时传递 &i
作为线程参数会导致额外的未定义行为。您的线程正在访问一个变量的地址,该变量由主线程不断更改,然后超出范围。
虽然前面的答案解决了您的代码问题,但这并不是您所问问题的原因,正如其作者所假设的那样。
问题是您将相同的参数传递给每个线程。线程正在打印当时 i
恰好是什么,一个你不断变化的值。 i
在线程尝试访问它时甚至可能不存在。
您需要传递不同的指针,而不是每次都传递相同的指针(例如,只为线程分配的指针,或者指向保证比线程寿命更长的数组的不同元素的指针)。或者,由于指针本身已被复制,您可以将足够小的整数转换为指针,然后让线程将指针转换回整数。
我有一个代码可以模拟串行与并行计算。但是,它似乎存在竞争条件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *foo(void *para) {
printf("This is Thread %d running...\n", *(size_t *)para);
fflush(stdout);
sleep(2);
}
int main(void)
{
/* loop version
for (size_t i = 0; i < 4; i++) {
foo((void *)&i);
}
*/
pthread_t pool[4];
for (size_t i = 0; i < 4; i++) {
pthread_create(&pool[i], NULL, foo, (void *)&i);
}
for (size_t i = 0; i < 4; i++) {
pthread_join(pool[i], NULL);
}
return 0;
}
输出:
[william@Notebook Downloads]$ time ./a.out
This is Thread 1 running...
This is Thread 2 running...
This is Thread 4 running...
This is Thread 3 running...
real 0m2.003s
user 0m0.003s
sys 0m0.000s
[william@Notebook Downloads]$ time ./a.out
This is Thread 3 running...
This is Thread 3 running...
This is Thread 2 running...
This is Thread 4 running...
real 0m2.003s
user 0m0.003s
sys 0m0.000s
发生竞争条件的大多数示例是当您有多个线程尝试写入共享值时。但是,这段代码中根本没有写操作。那么,为什么会这样呢?是不是for循环没有按顺序执行?
我什至没有读你的问题。我刚刚查看了您的代码并观察到一个巨大的问题,这可能是您所问问题的根本原因。看下面的循环代码:
pthread_t pool[4];
for (size_t i = 1; i <= 4; i++) {
pthread_create(&pool[i], NULL, foo, (void *)&i);
}
您有一个大小为 4 的 "array",但您正在遍历索引 [1..4]
。 C 和 C++ 数组从索引 0 开始,到 (len-1) 结束。换句话说,您应该遍历索引 [0..3]。向 pool[4]
中插入任何内容都会产生未定义的行为。
代替这一行:
for (size_t i = 1; i <= 4; i++) {
你想要这个:
for (size_t i = 0; i < 4; i++) {
对执行 pthread_join
语句的第二个循环应用相同的修复。
同时传递 &i
作为线程参数会导致额外的未定义行为。您的线程正在访问一个变量的地址,该变量由主线程不断更改,然后超出范围。
虽然前面的答案解决了您的代码问题,但这并不是您所问问题的原因,正如其作者所假设的那样。
问题是您将相同的参数传递给每个线程。线程正在打印当时 i
恰好是什么,一个你不断变化的值。 i
在线程尝试访问它时甚至可能不存在。
您需要传递不同的指针,而不是每次都传递相同的指针(例如,只为线程分配的指针,或者指向保证比线程寿命更长的数组的不同元素的指针)。或者,由于指针本身已被复制,您可以将足够小的整数转换为指针,然后让线程将指针转换回整数。