C <pthread.h> 将局部变量传递给线程已损坏

C <pthread.h> pass local variables to thread is broken

#include <stdio.h>
#include <pthread.h>

typedef struct {
    int threadNum;
}thread_args;

void thread_func(void*vargp){
    thread_args*id=(thread_args*)vargp;
    printf("%i\n",id->threadNum);
}

int main() {
    for(int i=0;i<20;i++) {
        pthread_t id;
        thread_args args;
        args.threadNum=i;
        pthread_create(&id,NULL,thread_func,(void*)&args);
    }
    pthread_exit(NULL);
    return 0;
}

改编自https://www.geeksforgeeks.org/multithreading-c-2/

所以这应该输出:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

但以随机顺序打乱以考虑线程的并发性。

这里的问题是它实际上打印出这个:

4
9
10
5
11
12
13
8
4
4
17
6
18
7
15
19
6
14
19
16

如您所见,有重复的数字,0-3 被简单地跳过了。

我以前在其他框架中做过并发,我也见过类似的问题:这里发生的是 i 作为引用传递(我认为!)所以当 for循环递增i,它在所有线程参数变量中递增。

我怎样才能避免这种情况?

注意:一切都 100% 正确链接,我在 macOS 上。

PS:抱歉,如果这是重复的,我对此不是很有经验。

这实际上是一种竞争条件。您将 void pointer 传递给参数结构,但(可能)每个参数结构都重复使用相同的内存地址。因此,当您以后访问它时,您很可能会读取修改后的内存。试试这个:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct {
    int threadNum;
}thread_args;

void thread_func(void* vargp){
    thread_args* id = (thread_args*)vargp;
    printf("%i\n", id->threadNum);

    free(vargp);
}

int main() {
    for(int i=0;i<20;i++) {
        pthread_t id;
        thread_args* args = malloc(sizeof(thread_args));
        args->threadNum = i;
        pthread_create(&id, NULL, thread_func, (void*)args);
    }
    pthread_exit(NULL);
    return 0;
}

感谢 Kamil Cuk 指出另一个竞争条件。

请注意,此代码段可能仍会泄漏,因为代码从未加入线程,因此可能永远不会调用 free()

您的 for 循环中有一个 UB。您正在创建一个名为 args 的变量,您在其中为其分配一个值,作为引用传递给您的线程,以供稍后执行,并在 for 循环结束时将其销毁。然后你再做一次,可能会覆盖这个区域。

为了解决这个问题,我建议这样修改:

int main() {
    thread_args args[20] = {0};
    pthread_t id[20] = {0};

    for(int i=0;i<20;i++) {
        args[i].threadNum=i;
        pthread_create(&id[i],NULL,thread_func,(void*)&args[i]);
    }

    for(int i = 0; i < 20; i++)
        pthread_join(id[i], NULL);
    return 0;
}