使用pthread未输出预期值:线程读取共享变量值异常原因
多线程执行顺序导致的变量值意外问题解析
首先,先把你的代码和遇到的问题理清楚:
你的C语言多线程代码:
void *thread(void *vargp) { int *ptr = (int*)vargp; printf("vargp = %p, *vargp = %d\n",vargp, *(int*)vargp); pthread_exit((void*)*ptr); } void *thread2(void *vargp) { int *ptr = (int*)vargp; *ptr = 0; pthread_exit((void*)31); } int main(){ int i = 42; pthread_t tid, tid2; pthread_create(&tid, NULL, thread, (void*)&i); printf("i = %d, &i = %p\n", i, &i); pthread_create(&tid2, NULL, thread2, (void*)&i); pthread_join(tid, (void**)&i); pthread_join(tid2, NULL); printf("at the end i = %d\n",i); }
你预期最后一行输出at the end i = 42,但实际得到的输出是:
i = 42, &i = 0x7ffcf3b65c4c vargp = 0x7ffcf3b65c4c, *vargp = 0 0
你疑惑的是:明明vargp的地址和i的地址完全一致,为什么读取到的*vargp是0而不是42?
核心问题:线程调度的不确定性
咱们得先明确一个关键知识点:操作系统对线程的调度顺序是完全没有保证的——哪怕你先调用pthread_create创建了thread,也不代表它会比后创建的thread2先执行。
在你的这次运行中,实际的执行顺序是这样的:
- 主线程初始化
i=42,调用pthread_create创建thread,但操作系统并没有立刻让thread开始运行,主线程继续往下走。 - 主线程执行
printf,输出了i的初始值42和它的地址。 - 主线程创建
thread2,这时候操作系统选择先调度thread2执行:thread2直接把i的值改成了0,然后退出。 - 直到这时候,操作系统才调度
thread线程执行,此时i已经被thread2改成0了,所以*vargp读取到的自然是0。 - 最后主线程调用
pthread_join(tid, (void**)&i),把thread线程的退出值(也就是0,因为*ptr当时是0)赋值给了i,所以最后printf输出的0其实是这个退出值(刚好和thread2修改的结果一致)。
额外补充:关于线程退出值的赋值
这里还要提一下pthread_join的第二个参数:你把&i强转成(void**)传递进去,意味着pthread_join会把线程的退出指针值转换成整数赋值给i。哪怕thread2没修改i,只要thread返回的是42,i最后也会是42;但在这次运行中,thread返回的是0,所以i最终被设置为0。
如何得到你预期的输出?
如果想保证thread先读取到i的初始值42,你需要用同步机制来控制线程的执行顺序,比如互斥锁(pthread_mutex_t):
#include <pthread.h> #include <stdio.h> pthread_mutex_t mutex; void *thread(void *vargp) { pthread_mutex_lock(&mutex); int *ptr = (int*)vargp; printf("vargp = %p, *vargp = %d\n",vargp, *(int*)vargp); pthread_exit((void*)*ptr); } void *thread2(void *vargp) { pthread_mutex_lock(&mutex); int *ptr = (int*)vargp; *ptr = 0; pthread_exit((void*)31); } int main(){ pthread_mutex_init(&mutex, NULL); // 先上锁,让thread2必须等thread解锁后才能执行 pthread_mutex_lock(&mutex); int i = 42; pthread_t tid, tid2; pthread_create(&tid, NULL, thread, (void*)&i); printf("i = %d, &i = %p\n", i, &i); pthread_create(&tid2, NULL, thread2, (void*)&i); pthread_join(tid, (void**)&i); pthread_mutex_unlock(&mutex); // 解锁允许thread2执行 pthread_join(tid2, NULL); printf("at the end i = %d\n",i); pthread_mutex_destroy(&mutex); }
这样就能确保thread先执行并读取到i=42,之后thread2才会修改i,最后i会被pthread_join赋值为42,输出符合你的预期。
内容的提问来源于stack exchange,提问作者drdot
相关产品推荐
相关产品推荐

