为什么竞争条件会在循环中发生?

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 在线程尝试访问它时甚至可能不存在。

您需要传递不同的指针,而不是每次都传递相同的指针(例如,只为线程分配的指针,或者指向保证比线程寿命更长的数组的不同元素的指针)。或者,由于指针本身已被复制,您可以将足够小的整数转换为指针,然后让线程将指针转换回整数。