如何使用Critical Section实现交替写入A/B的多线程程序?代码排查
问题分析与解决方案
看起来你想实现的是两个线程交替把整个数组完全覆盖成'A'和'B',而不是交替写入单个元素对吧?只靠Critical Section是不够的——它只能保证同一时间只有一个线程操作数组,但没法控制线程的执行顺序,这就是你代码运行异常的核心原因。
为什么原代码会异常?
Critical Section的作用是互斥,防止两个线程同时修改数组,但它不会帮你协调线程的执行节奏。比如:
- 可能线程A刚写了一半数组,线程B就抢到了锁接着写,导致数组里既有'A'又有'B';
- 也可能线程A写完一遍后反复抢锁写,线程B根本没机会执行,结果全是'A';
这些情况都不符合你“全A→全B→全A...”的预期。
修正方案:Critical Section + 事件同步
要实现严格的交替执行,我们需要在互斥的基础上加上同步信号,让一个线程写完后主动通知另一个线程可以开始。在Windows平台,用Event事件对象配合Critical Section是很合适的方案。下面是完整的修正代码:
#include <stdlib.h> #include <stdio.h> #include <windows.h> #define SIZE_TAB 200 #define LOOP_COUNT 5 // 控制交替的次数,可按需修改 volatile char program[SIZE_TAB]; CRITICAL_SECTION cs; HANDLE eventAComplete; // 通知B线程:A已经写完了 HANDLE eventBComplete; // 通知A线程:B已经写完了 // 写入'A'的线程函数 DWORD WINAPI WriteA(LPVOID param) { for (int i = 0; i < LOOP_COUNT; i++) { // 等待B线程完成(第一次循环时,B还没执行,所以初始让A直接运行) WaitForSingleObject(eventBComplete, INFINITE); // 进入临界区,保证整个数组写入操作是原子的 EnterCriticalSection(&cs); printf("Thread A is writing 'A' to array...\n"); for (int j = 0; j < SIZE_TAB; j++) { program[j] = 'A'; } // 打印前10个元素验证结果 printf("Current array (first 10): "); for (int j = 0; j < 10; j++) { printf("%c ", program[j]); } printf("\n"); LeaveCriticalSection(&cs); // 切换信号:重置自己的等待事件,触发B的执行信号 ResetEvent(eventBComplete); SetEvent(eventAComplete); } return 0; } // 写入'B'的线程函数 DWORD WINAPI WriteB(LPVOID param) { for (int i = 0; i < LOOP_COUNT; i++) { // 等待A线程完成 WaitForSingleObject(eventAComplete, INFINITE); EnterCriticalSection(&cs); printf("Thread B is writing 'B' to array...\n"); for (int j = 0; j < SIZE_TAB; j++) { program[j] = 'B'; } printf("Current array (first 10): "); for (int j = 0; j < 10; j++) { printf("%c ", program[j]); } printf("\n"); LeaveCriticalSection(&cs); // 切换信号:重置自己的等待事件,触发A的执行信号 ResetEvent(eventAComplete); SetEvent(eventBComplete); } return 0; } int main() { // 初始化临界区 InitializeCriticalSection(&cs); // 创建事件:手动重置事件,初始时eventBComplete设为有信号(让A先执行) eventAComplete = CreateEvent(NULL, TRUE, FALSE, NULL); eventBComplete = CreateEvent(NULL, TRUE, TRUE, NULL); // 创建两个工作线程 HANDLE hThreadA = CreateThread(NULL, 0, WriteA, NULL, 0, NULL); HANDLE hThreadB = CreateThread(NULL, 0, WriteB, NULL, 0, NULL); // 等待线程执行完毕 WaitForSingleObject(hThreadA, INFINITE); WaitForSingleObject(hThreadB, INFINITE); // 清理资源 CloseHandle(hThreadA); CloseHandle(hThreadB); CloseHandle(eventAComplete); CloseHandle(eventBComplete); DeleteCriticalSection(&cs); return 0; }
关键细节说明
- 事件的初始化:
CreateEvent的第三个参数是初始信号状态,我们把eventBComplete设为TRUE(有信号),这样线程A一开始就能执行;eventAComplete初始为FALSE(无信号),线程B会等待直到A写完。 - 临界区的范围:把整个数组的写入循环都放在
EnterCriticalSection和LeaveCriticalSection之间,确保一次写入是完整的,不会被另一个线程打断。 - 信号切换逻辑:每个线程写完后,会重置自己的等待事件(避免重复触发),并设置对方的事件,这样就能严格交替执行。
内容的提问来源于stack exchange,提问作者Milek
相关产品推荐
相关产品推荐

