VC++ 线程同步之临界区(CriticalSection)
所谓临界区就是同一时刻只能有一个线程访问的代码段。处于临界区的代码通常都是需要被多个线程访问,但又只能顺序访问的。
一般就是共享的数据。用于实现顺序访问临界区的方式有多种,互斥量、信号量、事件都可以实现。
Windows临界区,在同一个线程中是可以重复进入的,但是进入的次数与离开的次数必须相等。
C++互斥量则不允许同一个线程重复加锁,否则报异常。
效果几乎可以等同于c++11的mutex,可以保护一个代码段
使用方式
//创建:
CRITICAL_SECTION my_winsec;//创建windows中的临界区,类似与互斥量,使用前必须初始化
//初始化:(通常在类构造函数中初始化)
InitializeCriticalSection(&my_winsec);//初始化临界区
//临界区使用:
EnterCriticalSection(&my_winsec);//进入临界区(加锁)
myQueue.push_back(i);
LeaveCriticalSection(&my_winsec);//离开临界区(解锁)
代码示例
下面是使用Win32 API的CriticalSection实现线程同步的C++代码:
#include <iostream>
#include <windows.h>
using namespace std;
CRITICAL_SECTION g_cs; // 全局的临界区
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
EnterCriticalSection(&g_cs);
// 这里是需要同步的代码
cout << "Thread " << lpParam << " is running." << endl;
LeaveCriticalSection(&g_cs);
return 0;
}
int main()
{
InitializeCriticalSection(&g_cs);
HANDLE hThread[3];
for (int i = 0; i < 3; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)i, 0, NULL);
if (hThread[i] == NULL)
{
cout << "Thread " << i << " failed to create." << endl;
}
}
WaitForMultipleObjects(3, hThread, TRUE, INFINITE);
DeleteCriticalSection(&g_cs);
return 0;
}
在这个例子中,我们创建了一个全局的CriticalSection对象g_cs,这个对象被多个线程共享。线程执行需要同步的代码前,首先调用EnterCriticalSection函数获取临界区的访问权,执行完需要同步的代码后再调用LeaveCriticalSection函数释放临界区的访问权。这样就能确保多个线程不会同时访问临界区,从而达到线程同步的目的。
自动析构技术
在Windows
临界区中没有类似于C++ 11
中的std::lock_guard
的自动管理临界区上锁与取消锁的API
,可以根据其特点手写一个:
//自动释放Windows临界区,防止忘记解锁,即实现一个Windows的lock_guard();
class CWinLock
{
public:
CWinLock(CRITICAL_SECTION *CS)
{
m_CS = CS;
EnterCriticalSection(m_CS);
}
~CWinLock()
{
LeaveCriticalSection(m_CS);
}
private:
CRITICAL_SECTION* m_CS;
};
怎么使用:
CRITICAL_SECTION my_winsec;//创建windows中的临界区
CWinLock cw(&my_winsec);
myQueue.push_back(i);