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;
}
#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;
}