1.简介
关键段是一小段代码,它在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。
使用的函数:
等待指定临界区对象的所有权。当调用线程被授予所有权时,函数返回。
void EnterCriticalSection(
[in, out] LPCRITICAL_SECTION lpCriticalSection
);
释放指定临界区对象的所有权。
void LeaveCriticalSection(
[in, out] LPCRITICAL_SECTION lpCriticalSection
);
初始化临界区对象。在调用EnterCriticalSection之前必须调用初始化函数。
void InitializeCriticalSection(
[out] LPCRITICAL_SECTION lpCriticalSection
);
释放资源。
void DeleteCriticalSection(
[in, out] LPCRITICAL_SECTION lpCriticalSection
);
2.示例
此示例,创建了两个线程。对全局变量g_sum进行累加操作。
// Interlocked.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
DWORD WINAPI add0(LPVOID);
DWORD WINAPI add1(LPVOID);
int g_count = 10;
long g_sum = 0;
CRITICAL_SECTION g_cs;
int main()
{
HANDLE aThread[2];
DWORD ThreadID;
InitializeCriticalSection(&g_cs);
aThread[0] = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)add0,
NULL, // no thread function arguments
0, // default creation flags
&ThreadID); // receive thread identifier
if (aThread[0] == NULL)
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
aThread[1] = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)add1,
NULL, // no thread function arguments
0, // default creation flags
&ThreadID); // receive thread identifier
if (aThread[1] == NULL)
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
WaitForMultipleObjects(2, aThread, TRUE, INFINITE);
// Close thread and mutex handles
for (int i = 0; i < 2; i++)
CloseHandle(aThread[i]);
DeleteCriticalSection(&g_cs);
return 0;
}
DWORD WINAPI add0(LPVOID lpParam)
{
UNREFERENCED_PARAMETER(lpParam);
EnterCriticalSection(&g_cs);
g_sum = 0;
for (int i = 0; i < g_count; i++)
{
g_sum += i;
}
printf("Thread %d g_sum = %d\n",
GetCurrentThreadId(), g_sum);
LeaveCriticalSection(&g_cs);
return g_sum;
}
DWORD WINAPI add1(LPVOID lpParam)
{
UNREFERENCED_PARAMETER(lpParam);
EnterCriticalSection(&g_cs);
g_sum = 0;
for (int i = 0; i < g_count; i++)
{
g_sum += i;
}
printf("Thread %d g_sum = %d\n",
GetCurrentThreadId(), g_sum);
LeaveCriticalSection(&g_cs);
return g_sum;
}
首先需要定义一个CRITICAL_SECTION g_cs数据结构。然后把需要访问共资源(g_sum)的代码放在EnterCriticalSection和LeaveCriticalSection之间,注意传入的g_cs是地址。
当不能用Interlocked函数解决同步问题的时候,应该试一试关键段。它的最大好处在于它们非常容易使用,而且它们内部也使用了Interlocked函数,最大缺点在于它们无法用来在多个进程之前对线程进行同步。
3.互斥量内核对象
4.互斥量对象和关键段的异同
互斥量对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而关键代码段则属于用户方式对象。这意味着互斥对象的运行速度比关键代码段要慢。但是这也意味着不同进程中的多个线程能够访问单个互斥量对象,并且这意味着线程在等待访问资源时可以设定一个超时值。
特性 | 互斥量对象 | 关键段 |
运行速度 | 慢 | 快 |
是否能够跨进程边界来使用 | 是 | 否 |
声明 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化 | hmtx = CreateMutex(NULL,FALSE,NULL); | InitializeCriticalSection(&es); |
清除 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限等待 | WaitForSingleObject(hmtx ,INFINITE); | EnterCriticalSection(&cs); |
0等待 | WaitForSingleObject(hmtx , 0); | EnterCriticalSection(&cs); |
任意等待 | WaitForSingleObject(hmtx,dwMilliseconds); | 不能 |
释放 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
是否能够等待其他内核对象 | 是(使用WaitForMultipleObjects或类似的函数) | 否 |