C++中使用CriticalSection实现线程同步的完整示例
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基础操作的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









