1、临界区对象
临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录一些同步信息,确保在同一段时间只有一个线程访问数据段中的数据。
临界区对象相关函数:
// 初始化临界区对象资源
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 进入临界区
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 离开临界区
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 删除临界区对象资源
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函数参数lpCriticalSection为指向临界区源的对象
2、临界区对象实例
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
int g_Cnt = 0; // 计数器
CRITICAL_SECTION g_cs; // 临界区对象
BOOL bFlag = FALSE;// 判断是否创建辅助线程
UINT _stdcall ThreadFunc(LPVOID lpParam);
int main()
{
UINT uId[2];
HANDLE h[2];
::InitializeCriticalSection(&g_cs); // 初始化临界区资源
h[0] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[0]);
h[1] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[1]);
// 标志创建了辅助线程
bFlag = TRUE;
// 主线程休息1000ms,让出CPU的使用权,让我们的辅助线程有运行的机会
Sleep(1000);
// 主线程运行
bFlag = FALSE;
::WaitForMultipleObjects(2, h, TRUE, INFINITE);
// 关闭句柄
CloseHandle(h[0]);
CloseHandle(h[1]);
::DeleteCriticalSection(&g_cs); // 清除临界区资源
printf("g_Cnt = %d\n", g_Cnt);
system("pause");
return 0;
}
UINT _stdcall ThreadFunc(LPVOID lpParam)
{
if (bFlag)
{
::EnterCriticalSection(&g_cs);
for (int i =0;i< 10000;i++)
g_Cnt++;
::LeaveCriticalSection(&g_cs);
}
return 0;
}
临界区对象能很好的保护共享数据,但是它不能够用于进程之间资源的锁定,因为它不是内核对象,如果要在进程间维持线程的同步,可以使用事件内核对象。
3、互锁函数
互锁函数为同步访问多线程共享变量提供了一个简单的机制。
如果变量在共享内存,不同进程的线程也可以使用此机制。
互锁函数:
// 原子自加操作
InterlockedIncrement
// 原子自减操作
InterlockedDecrement
InterlockedExchangeAdd
InterlockedExchangePointer
4、实例
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
int g_Cnt = 0; // 计数器
BOOL bFlag = FALSE;// 判断是否创建辅助线程
UINT _stdcall ThreadFunc(LPVOID lpParam);
int main()
{
UINT uId[2];
HANDLE h[2];
h[0] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[0]);
h[1] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[1]);
// 标志创建了辅助线程
bFlag = TRUE;
// 主线程休息1000ms,让出CPU的使用权,让我们的辅助线程有运行的机会
Sleep(1000);
// 主线程运行
bFlag = FALSE;
::WaitForMultipleObjects(2, h, TRUE, INFINITE);
// 关闭句柄
CloseHandle(h[0]);
CloseHandle(h[1]);
printf("g_Cnt = %d\n", g_Cnt);
system("pause");
return 0;
}
UINT _stdcall ThreadFunc(LPVOID lpParam)
{
if (bFlag)
{
for (int i = 0; i < 10000; i++)
// 原子自加操作,不会被中断
InterlockedIncrement((long *)&g_Cnt);
}
return 0;
}
通过本节的学习,我们知道了如何控制关键的代码段不被系统打断,可以通过临界区或者互锁函数来实现关键代码段不被系统打断,可以保证数据得出正确的结果!