C语言双线程用Mutex和条件变量打印序列异常:仅输出B序列
线程同步问题:mutex+条件变量打印指定模式失败分析
问题描述
需要用两个线程结合互斥锁(mutex)和条件变量打印序列A0B1A2B3A4B5A6B7A8B9,模式存储在字符数组中,用全局变量string_index记录当前打印位置。预期逻辑:
- 先创建线程A,它先锁定mutex并执行打印,随后休眠等待信号
- 线程B拿到mutex后打印下一个序列,发送信号唤醒A,循环往复
但实际运行后只输出B0B1B2B3B4B5B6B7B8B9,线程A完全没执行。
原代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int string_index = 0; char string_to_print[10] = "0123456789"; pthread_mutex_t lock; pthread_cond_t cond; void *funcA(void *arg) { pthread_mutex_lock(&lock); // Lock mutex while (1) { printf("A%c", string_to_print[string_index]); string_index++; // Sleep and wait for signal pthread_cond_wait(&cond, &lock); } } void *funcB(void *arg) { while (1) { pthread_mutex_lock(&lock); // Lock mutex printf("B%c", string_to_print[string_index]); string_index++; if(string_index > 9) { string_index = 0; printf("\n"); } // Unlock mutex pthread_mutex_unlock(&lock); // Send cond var signal pthread_cond_signal(&cond); } } int main(void) { // Initialize mutex lock if (pthread_mutex_init(&lock, NULL) != 0) { printf("\n mutex init has failed\n"); return 1; } // Initialize conditional variable if (pthread_cond_init(&cond, NULL) != 0) { printf("cond var init failed"); return 1; } pthread_t A, B; // Create thread A if (pthread_create(&A, NULL, &funcA, NULL) != 0) { printf("Error creating thread"); exit(-1); } // Create thread B if (pthread_create(&B, NULL, &funcB, NULL) != 0) { printf("Error creating thread"); exit(-1); } sleep(20); /* Sleep 20 seconds to allow time for the threads to run before terminating them with pthread_cancel() */ pthread_cancel(A); pthread_cancel(B); pthread_exit(NULL); pthread_mutex_destroy(&lock); pthread_cond_destroy(&cond); }
错误原因分析
线程执行顺序的误解:
线程创建后的执行顺序由操作系统调度器决定,并非先创建的线程一定会先运行。原代码假设线程A会先锁定mutex,但实际可能线程B先抢占到mutex,直接开始循环打印,而线程A一直在等待mutex解锁,根本没机会执行第一次打印。条件变量使用不规范:
pthread_cond_wait()必须配合条件判断使用,原代码中没有判断当前是否轮到该线程执行,即使线程被唤醒,也无法保证执行顺序。此外,缺少条件判断也无法处理操作系统的「虚假唤醒」问题(线程可能被无理由唤醒)。线程A的逻辑缺陷:
线程A在第一次打印后就进入pthread_cond_wait(),但线程B在打印后解锁mutex并发送信号,线程A被唤醒后会直接再次打印——但此时string_index已经被线程B递增过,导致打印内容错误;更糟的是,线程B的循环会反复尝试锁定mutex,可能在A唤醒后再次抢占锁,继续打印。
修正方案及代码
核心是添加一个「轮次标记」变量,明确当前哪个线程应该执行,配合条件变量实现严格的交替执行:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int string_index = 0; char string_to_print[10] = "0123456789"; pthread_mutex_t lock; pthread_cond_t cond; int turn = 0; // 0: 线程A执行,1: 线程B执行 void *funcA(void *arg) { while (1) { pthread_mutex_lock(&lock); // 等待轮到自己执行,处理虚假唤醒 while (turn != 0) { pthread_cond_wait(&cond, &lock); } // 打印当前内容并更新索引 printf("A%c", string_to_print[string_index]); string_index++; // 切换轮次为线程B turn = 1; // 唤醒线程B pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); // 重置索引并换行,准备下一轮 if (string_index >= 10) { string_index = 0; printf("\n"); // 重置轮次为A,确保下一轮从A开始 pthread_mutex_lock(&lock); turn = 0; pthread_mutex_unlock(&lock); sleep(1); // 延迟方便观察每一轮输出 } } } void *funcB(void *arg) { while (1) { pthread_mutex_lock(&lock); // 等待轮到自己执行,处理虚假唤醒 while (turn != 1) { pthread_cond_wait(&cond, &lock); } // 打印当前内容并更新索引 printf("B%c", string_to_print[string_index]); string_index++; // 切换轮次为线程A turn = 0; // 唤醒线程A pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); } } int main(void) { if (pthread_mutex_init(&lock, NULL) != 0) { printf("\n mutex init has failed\n"); return 1; } if (pthread_cond_init(&cond, NULL) != 0) { printf("cond var init failed"); return 1; } pthread_t A, B; if (pthread_create(&A, NULL, &funcA, NULL) != 0) { printf("Error creating thread"); exit(-1); } if (pthread_create(&B, NULL, &funcB, NULL) != 0) { printf("Error creating thread"); exit(-1); } sleep(5); // 缩短等待时间,避免过长阻塞 pthread_cancel(A); pthread_cancel(B); pthread_mutex_destroy(&lock); pthread_cond_destroy(&cond); return 0; }
关键修改点
- 添加
turn变量明确轮次,确保线程只在自己的回合执行打印 - 用
while循环包裹pthread_cond_wait(),重新检查条件以处理虚假唤醒 - 每个线程打印后切换轮次,唤醒对方线程再释放mutex,保证执行顺序严格交替
- 索引重置后同步轮次,确保下一轮从线程A开始
内容的提问来源于stack exchange,提问作者Sliferslacker
相关产品推荐
相关产品推荐

