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

多线程临界区使用疑问:CRITICAL_SECTION无法实现连续打印无干扰

解决临界区实现多线程连续打印的问题

嘿,我来帮你梳理下临界区使用时容易踩的坑,你要实现每个线程连续打印5个数字不被干扰的需求,核心问题大概率是临界区的包裹范围或者作用域没处理对。我先给你拆解常见的错误原因,再给你正确的实现示例:

常见错误点分析

  • 临界区包裹范围错误:很多人会把EnterCriticalSection和LeaveCriticalSection放在单个打印语句的前后,这样每个数字打印完就释放临界区,其他线程立刻就能插进来,自然没法连续打印5个数字。正确的做法是把整个5次打印的循环都放在临界区的保护范围内。
  • 临界区对象作用域错误:如果每个线程自己定义一个CRITICAL_SECTION结构体,那各个线程的临界区是独立的,完全起不到互斥作用。必须保证所有线程共享同一个临界区实例。
  • 初始化/销毁时机错误:如果在线程创建后才初始化临界区,或者线程还在运行就销毁了临界区,都会导致临界区无法正常工作。必须在创建线程前完成初始化,所有线程执行完毕后再销毁。

正确实现示例代码

#include <windows.h>
#include <stdio.h>

// 全局临界区对象,确保所有线程都能访问到同一个实例
CRITICAL_SECTION g_printCs;

DWORD WINAPI PrintThreadProc(LPVOID lpParam) {
    int threadNum = *(int*)lpParam;

    // 进入临界区:整个连续打印的逻辑都被保护
    EnterCriticalSection(&g_printCs);
    printf("=== 线程%d开始连续打印 ===\n", threadNum);
    for (int i = 0; i < 5; i++) {
        printf("线程%d: 数字%d\n", threadNum, i + 1);
        // 模拟耗时操作,让线程切换的效果更明显
        Sleep(150);
    }
    printf("=== 线程%d打印结束 ===\n", threadNum);
    // 离开临界区:释放锁,让其他线程可以进入
    LeaveCriticalSection(&g_printCs);

    return 0;
}

int main() {
    // 初始化临界区
    InitializeCriticalSection(&g_printCs);

    HANDLE threadHandles[3];
    int threadNums[3] = {1, 2, 3};

    // 创建3个测试线程
    for (int i = 0; i < 3; i++) {
        threadHandles[i] = CreateThread(
            NULL,           // 默认安全属性
            0,              // 默认栈大小
            PrintThreadProc,// 线程函数
            &threadNums[i], // 传递给线程的参数
            0,              // 立即运行线程
            NULL            // 不获取线程ID
        );
    }

    // 等待所有线程执行完毕
    WaitForMultipleObjects(3, threadHandles, TRUE, INFINITE);

    // 销毁临界区
    DeleteCriticalSection(&g_printCs);

    // 关闭线程句柄释放资源
    for (int i = 0; i < 3; i++) {
        CloseHandle(threadHandles[i]);
    }

    return 0;
}

代码关键点说明

  1. 共享临界区对象:g_printCs是全局变量,所有线程都使用这个实例,确保互斥逻辑生效。
  2. 正确的临界区范围:EnterCriticalSection在循环前调用,LeaveCriticalSection在循环结束后调用,保证整个连续打印的过程不会被其他线程打断。
  3. 正确的生命周期:主线程先初始化临界区,再创建线程;等所有线程都执行完毕后,才销毁临界区,避免线程运行时临界区失效。

另外还要注意:如果线程执行过程中可能抛出异常(比如C的异常),要确保LeaveCriticalSection一定会被调用,否则会导致死锁。可以用RAII封装临界区(比如C里写个类,构造时Enter,析构时Leave),避免手动管理的失误。

内容的提问来源于stack exchange,提问作者ezviagintcev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:26:23