pthread_create场景主线程卡死原因及示例代码正确性分析
C多线程代码主线程卡死问题解析
涉及问题代码
int count = numThreads; void* bar(void* arg) { /* do some stuff */ --count; } int main(int argc, char* argv[]) { /* ... initialize everything ... */ for (i = 0 ; i < numThreads; ++i) pthread_create(&thread[i],NULL,bar,NULL); while (count > 0) sleep(1); }
核心疑问
线程执行函数bar内部不存在无限循环逻辑,为何会出现主线程卡死、无法执行完main函数的情况?
问题根因
哪怕bar没有死循环、所有子线程都能正常执行完自身逻辑,主线程依然可能卡在等待循环中无法退出,核心原因有两点:
- 共享变量无并发保护,既会出现更新丢失,也存在内存可见性问题
全局变量count是多线程共享的资源,代码既没有用互斥锁保护对它的读写操作,也没有将它声明为原子类型。首先--count本身不是原子操作,多线程并发执行时极易出现更新丢失:比如两个线程同时读取到count值为2,各自减1后写回结果1,原本两次递减后count应该为0,最终实际值为1,所有子线程执行完毕后count依然大于0,主线程自然无法退出循环。
就算没有出现更新丢失,还存在内存可见性的缺陷:C语言内存模型不保证普通共享变量的修改在多线程之间即时可见。编译器做优化时,如果发现主线程循环内部没有修改count的逻辑,甚至会直接把while (count > 0)优化为恒真的死循环,根本不会每次循环都重新从内存读取count的最新值。即便编译器没有做这类优化,多核CPU场景下不同核心有独立的缓存,没有做缓存同步的前提下,主线程所在核心可能一直读到缓存中count的旧值,永远等不到count变为0的时刻。 sleep函数不提供内存同步语义
不存在“调用sleep会刷新缓存、同步内存最新值”的机制,POSIX标准从未规定sleep需要承担内存同步的职责,主线程每次从sleep唤醒后,依然可能读到count的旧值,持续循环无法退出。
题目选项正误判断
- (א) 代码正确:错误,存在明确的并发逻辑缺陷
- (ב) 若运行过程中无信号发送至该进程则代码正确:错误,即使没有信号触发,编译器优化、CPU缓存不可见、更新丢失的问题依然存在,会触发卡死
- (ג) 代码错误,主线程可能无法执行结束:正确,对应上述所有可能触发卡死的场景
- (ד) 代码错误,主线程可能在其余所有线程执行完成前提前结束:错误,count初始值等于总线程数,每个子线程仅执行一次count递减操作,不存在count提前变为0的逻辑
内容的提问来源于stack exchange,提问作者Hussain Mussa
相关产品推荐
相关产品推荐

