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

时间:2019-03-26 23:49:26

标签: c pthreads

#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被作为引用传递(我认为!),因此{{1 }}循环递增for,它在所有线程参数变量中都递增。

如何避免这种情况?

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

PS:对不起,如果重复的话,我对此并不十分了解。

2 个答案:

答案 0 :(得分:4)

您的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;
}

答案 1 :(得分:1)

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

#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()