Windows核心编程--线程同步--关键段

InterLocked系列函数对变量进行原子操作,执行得几块,通常只占用几个CPU周期(小于50),不需要再用户模式和内核模式之间切换,这个切换通常需要占用1000个周期以上。

InterLockedExchangeAdd既可以做加法也可以做减法。

LONG InerLockedExchange( PLONG volatile plTarget, LONG lValue )

该函数会把第一个参数所指向的内存地址的当前值,以原子方式替换为第二个参数指定的值,返回原来的值。下面用该函数实现旋转锁。

BOOL g_fResourceInUse = FALSE;


VOID SpinLockFun(VOID)
{
while(InterlockedExchange( (unsigned volatile *)&g_fResourceInUse,TRUE) == TRUE )
{
Sleep(0);
}
_tprintf(_T("Have get the resource\n"));


InterlockedExchange((unsigned volatile *)&g_fResourceInUse, FALSE);
}


糟糕的数据结构设计:

typedef struct _CUSTINFO
{
DWORD dwCustomerID;//只读,经常访问
int  nBalanceDue;  //读写
TCHAR szName[100]; //只读,经常访问
FILETIME ftLstOrderDate; //读写
}CUSTINFO, *PCUSTINFO;

优秀的数据结构设计,把读写数据分开:

struct __declspec(align(CACHE_ALIGN)) _CUSTINFO {
DWORD dwCustomerID;  //Readonly
TCHAR szName[100]; //Readonly

 //将以下两个字段放在不同的缓存行中
__declspec(align(CACHE_ALIGN))
int  nBalanceDue;  //读写
FILETIME ftLstOrderDate; //读写
};


关键段是对一段代码进行保护,相比于Inerlocked系列函数,功能更强大,但是关键段有一个缺点,就是无法再多个进程之间对线程进行同步。

const int COUNT = 1000;
int g_num = 0;
CRITICAL_SECTION g_cs;


UINT WINAPI FirstThread(PVOID pvParam)
{
EnterCriticalSection(&g_cs);
g_num = 0;
for (int  i = 0; i <= COUNT; i++)
{
g_num += i;
}
_tprintf(_T("FirstThread g_num=%d TheadID= %d\n"), g_num, GetCurrentThreadId());
LeaveCriticalSection(&g_cs);
return g_num;
}

UINT WINAPI SecondThread(PVOID pvParam)
{
EnterCriticalSection(&g_cs);
g_num = 0;
for (int i = 0; i <= COUNT; i++)
{
g_num += i;
}
_tprintf(_T("SecondThread g_num=%d TheadID= %d\n"), g_num, GetCurrentThreadId());
LeaveCriticalSection(&g_cs);
return g_num;
}
int _tmain( int argc, TCHAR* argv[] )
{
//DumpModule();
//DumpEnvStrings();
//ProcessInherit();
//StartRestrictedProcess();
InitializeCriticalSection(&g_cs);//初始化关键段
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, FirstThread, NULL, 0, NULL);
HANDLE hThread_2 = (HANDLE)_beginthreadex(NULL, 0, SecondThread, NULL, 0, NULL);
HANDLE handle[2];
handle[0] = hThread;
handle[1] = hThread_2;
WaitForMultipleObjects(2, handle, TRUE, INFINITE);
DeleteCriticalSection(&g_cs);//清理CRITICAL_SECTION结构体
    return 0;
}

输出结果如下:


当线程尝试进入一个关键段,但这个关键段正被另一个线程占用的时候,函数会立即把调用线程切换到等待状态,这意味着线程必须从用户模式切换到内核模式,大约1000个CPU周期,这个切换开销非常大。为了提高关键段的性能,可以吧旋转锁合并到关键段中,因此,当调用EnterCriticalSection的时候,它会用一个旋转锁不停地循环,尝试在一段时间内获得对资源的访问权,只有尝试失败的时候,线程才会切换到内核模式并进入等待状态。

BOOL InitializeCriticalSectionAndSpinCount(

PCRITICAL_SECTION pcs;

DWORD dwSpinCount

);

如果主机只有一个处理器,函数会忽略dwSpinCount这个参数,如果在多处理器中,dwSpinCount参数一般设置为4000;

InitializeCriticalSection函数返回值是VOID,但是这个函数可能会失败,原因是该函数内部会分配一块内存,如果内存分配失败,那么函数会抛出STATUS_NO_MEMORY异常,可以通过结构化异常来捕获该异常。

在内存不足的情况下,可能会发生争夺关键段的现象,系统无法创建所需的事件内核对象,EnterCriticalSection此时会抛出EXCEPTION_INVALID_HANDLE异常。

因此,在初始化关键段的时候,最好用InitializeCriticalSectionAndSpinCount函数初始化关键段,并将dwSpinCount的最高位设为1。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值