C++中使用CriticalSection实现线程同步的完整示例

小鹿同学
小鹿同学
发布时间:2026-10-14 | 3人浏览过
原创

C++多线程编程中常面临数据竞争风险,使用Windows API中的Critical Section(临界区)是实现轻量级线程同步的有效手段。本文通过一个双线程累加全局变量的实例,演示如何正确初始化、进入、离开并销毁临界区,确保最终结果的一致性。

核心原理与操作步骤

在多线程环境下,多个线程同时读写共享资源会导致不可预知的结果。临界区是一种互斥机制,同一时刻只允许一个线程执行被保护的代码段。以下是实现同步的关键步骤:

第1步:定义与初始化临界区对象

目的:创建用于保护共享资源的同步对象。 入口/操作:声明类型为 CRITICAL_SECTION 的全局变量,并在主函数开始时调用 ::InitializeCriticalSection(&cs) 进行初始化。

第2步:构建受保护的线程过程

目的:确保线程在执行关键逻辑时独占资源。 入口/操作:在线程函数入口处调用 ::EnterCriticalSection(&cs),在退出前调用 ::LeaveCriticalSection(&cs)。这将包裹住所有对共享变量的读写操作。

第3步:创建并等待线程结束

目的:启动工作线程并确保主线程等待其完成同步操作。 入口/操作使用 CreateThread 创建线程,并通过 WaitForMultipleObjects 阻塞主线程直到所有子线程返回。在唤醒标志变量后等待,保证计算完成。

第4步:清理与销毁

目的:释放系统资源,防止内存泄漏。 入口/操作:在确认所有线程结束后,调用 ::DeleteCriticalSection(&cs) 销毁临界区对象,并关闭线程句柄。

完整代码实现

以下代码展示了如何结合上述步骤实现一个简单的计数器同步。两个线程同时递增全局变量 g_cnt1 和 g_cnt2,由于使用了临界区,最终这两个值应当相等且等于总迭代次数。

#include "stdafx.h"
#include <Windows.h>
 
DWORD g_cnt1;
DWORD g_cnt2;
BOOL g_bContinue = TRUE;
CRITICAL_SECTION cs;
 
// 线程函数:执行受保护的计数操作
DWORD WINAPI ThreadProc(__in LPVOID lpParameter) 
{
  // 进入临界区,获取独占访问权
  ::EnterCriticalSection(&cs);
  while(g_bContinue)
  {
    // 此时只有当前线程能访问这些全局变量
    g_cnt1++;
    g_cnt2++;
  }
  // 离开临界区,允许其他线程访问
  ::LeaveCriticalSection(&cs);
  return 0;
} 
 
int _tmain(int argc, _TCHAR* argv[])
{
  HANDLE hThread[2];
  
  // 初始化全局计数器和临界区
  g_cnt1 = g_cnt2 = 0;
  ::InitializeCriticalSection(&cs);

  // 创建两个相同的线程
  hThread[0] = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
  hThread[1] = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

  // 让线程运行一段时间(约1秒),然后停止
  Sleep(1000);
  g_bContinue = FALSE;
  
  // 等待所有线程结束
  ::WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
  
  // 打印结果验证同步有效性
  printf("g_cnt1=%d\n", g_cnt1); 
  printf("g_cnt2=%d\n", g_cnt2);

  // 清理资源
  ::DeleteCriticalSection(&cs);
  ::CloseHandle(hThread[0]);
  ::CloseHandle(hThread[1]);
  
  return 0;
}

关键点解析与注意事项

  • 成对调用:EnterCriticalSection 和 LeaveCriticalSection 必须成对出现。如果在业务逻辑中发生异常或提前返回而忘记调用 Leave,将导致死锁。在实际复杂工程中,建议使用 RAII 包装类(如 C++11 起的部分库扩展或第三方库)来自动管理生命周期。
  • 性能考量:临界区在用户模式下运行,没有内核切换开销,因此比互斥量(Mutex)更快。它适用于线程竞争不激烈、持有锁时间短的场景。
  • 初始化检查:在使用临界区之前务必确保 InitializeCriticalSection 成功,否则后续操作可能导致未定义行为。
  • 循环条件安全:示例中使用全局布尔值 g_bContinue 作为退出标志。在真实高并发场景中,建议对标志变量的读取和修改也考虑原子性或适当的同步保护,尽管在此简单示例中因临界区的存在,外部修改仅在等待所有线程结束后进行,是安全的。

以上就是C++使用CriticalSection实现线程同步的详细内容,更多关于C++多线程开发与Windows API基础操作的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表