Windows多线程编程(7)--原子操作

来源:互联网 发布:js数组的map方法 编辑:程序博客网 时间:2024/05/23 00:07

今天介绍一种低层次的多线程同步机制--原子操作(Atomic operation)。原子操作保证同一时间只能有一个线程对某个变量进行操作。通常一条C/C++语句对应多个汇编指令,而处理器的中断可能发生在任意一条汇编指令处。因此,当一个线程访问某个变量时,可能会有另一个线程也在访问,这就造成了数据的不一致。

下面给出一个例子。

#include <Windows.h>#include <iostream> DWORD WINAPI ThreadFunc(LPVOID p);int num = 0; int main(int argc, char **argv){    for (int t = 0; t < 100; t++)    {        num = 0;         const int N = 20;        HANDLE hThreadArray[N];        for (int i = 0; i < N; i++)        {            hThreadArray[i] = CreateThread(NULL, 0, ThreadFunc, (LPVOID)i, 0, NULL);        }         WaitForMultipleObjects(N, hThreadArray, TRUE, INFINITE);        for (int i = 0; i < N; i++)        {            CloseHandle(hThreadArray[i]);        }         std::cout << "num = " << num << std::endl;    }     return 0;} DWORD WINAPI ThreadFunc(LPVOID p){    Sleep(10);    num++;    Sleep(10);    num--;    Sleep(10);    num++;    return 0;}

上面的程序每次启动20个子线程,每个子线程对全局变量num加1。模拟100次,程序运行结果并不总是20。这是因为ThreadFunc中的num++和num--操作不是原子操作,在执行的过程中发生了中断。

下面是我的计算机的运行结果。


为了实现变量操作的同步,Windows提供了一系列原子操作函数。下面详细介绍这些函数。


1. InterlockedIncrement:指定的LONG型变量增加1

WINBASEAPILONGWINAPIInterlockedIncrement (    __inout LONG volatile *lpAddend    );

lpAddend:指向自增变量的指针。

返回值:返回自增后的值。

LONG定义在头文件WinNT.h中,如下所示。

#ifndef VOID#define VOID voidtypedef char CHAR;typedef short SHORT;typedef long LONG;#if !defined(MIDL_PASS)typedef int INT;#endif#endif
2. InterlockedDecrement:指定的LONG型变量减少1
WINBASEAPILONGWINAPIInterlockedDecrement (    __inout LONG volatile *lpAddend    );

lpAddend:指向自减变量的指针。

返回值:返回自减后的值。


3. InterlockedExchange:对一个LONG型变量赋值。
WINBASEAPILONGWINAPIInterlockedExchange (    __inout LONG volatile *Target,    __in    LONG Value    );

Target:指向赋值变量的指针。

Value:赋给Target指向变量的值。

返回值:返回未赋值之前Target指向的变量的值。

当我们需要对指针赋值时,可以用InterlockedExchangePointer函数。它其实是一个基于InterlockedExchange函数的宏。这是因为对于32位程序,指针就是一个LONG型的变量。

#define InterlockedExchangePointer(Target, Value) \    (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))


4. InterlockedExchangeAdd:对一个LONG型变量做加法运算。
WINBASEAPILONGWINAPIInterlockedExchangeAdd (    __inout LONG volatile *Addend,    __in    LONG Value    );

Addend:指向LONG型变量的指针。

Value:加数。执行后,*Addend += Value。

返回值:返回未做加法之前,Addend指向的变量的值。


5. InterlockedCompareExchange:比较赋值函数。
WINBASEAPILONGWINAPIInterlockedCompareExchange (    __inout LONG volatile *Destination,    __in    LONG Exchange,    __in    LONG Comperand    );

如果 *Destination == Comperand,*Destination = Exchange;否则,不执行任何操作。

返回值:*Destination未修改之前的值。

对于高版本的Windows,还定义了LONGLONG类型的InterlockedCompareExchange64函数,如下所示。

#if (_WIN32_WINNT >= 0x0502) WINBASEAPILONGLONGWINAPIInterlockedCompareExchange64 (    __inout LONGLONG volatile *Destination,    __in    LONGLONG Exchange,    __in    LONGLONG Comperand    ); #endif
然后基于该函数,又定义了3个位运算函数(And,Or,Xor)和上述1~4个函数的LONGLONG类型的版本。我们仅以And函数位列,列出代码。

FORCEINLINELONGLONGInterlockedAnd64 (    __inout LONGLONG volatile *Destination,    __in    LONGLONG Value    ){    LONGLONG Old;     do {        Old = *Destination;    } while (InterlockedCompareExchange64(Destination,                                          Old & Value,                                          Old) != Old);     return Old;}
最后,当我们使用C++语言的时候,Windows对以上函数进行了重载,从而支持其它类型的变量。我们仅以InterlockedIncrement函数为例,列出在WinBase.h文件中的相关代码。

#if defined(__cplusplus) /* { */ extern "C++" { FORCEINLINEunsignedInterlockedIncrement(    __inout __drv_interlocked unsigned volatile *Addend    ){    return (unsigned) InterlockedIncrement((volatile long*) Addend);} FORCEINLINEunsigned longInterlockedIncrement(    __inout __drv_interlocked unsigned long volatile *Addend    ){    return (unsigned long) InterlockedIncrement((volatile long*) Addend);}


原文:http://hi.baidu.com/herohbc/item/61c973eddbecb0216dabb805

0 0
原创粉丝点击