You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:10:15