原子锁(原子访问):一个线程在访问某个资源的同时必须确保其他线程不会同时访问此资源。
没有实现原子锁的结果:
//定义一个全局变量 long g_lx = 0; DWORD WINAPI ThreadFunc1(PVOID pvParam){ for( int index=0; index<10000; index++ ) { g_lx++; //g_lx加1; } return 0; } DWORD WINAPI ThreadFunc2(PVOID pvParam){ for( int index=0; index<10000; index++ ) { g_lx++; //g_lx加1; } return 0; }
程序代码欲把g_lx累加到20000;而结果为小于20000. 原因是:g_lx++;不是原子级别的。编译器编译g_lx++的汇编代码近似为:
MOV EAX,[g_lx] //将g_x的值赋值到EAX寄存器中 INC EAX //EAX寄存器进行+1操作,原子级别 MOV [g_lx], EAX //将EAX寄存器中的值赋值到g_lx中
重点是在多个线程同时操作一个资源g_lx ++时,可能会出现如下:
MOV EAX,[g_lx] //线程1 INC EAX //线程1 MOV EAX,[g_lx] //线程2 INC EAX //线程2 MOV [g_lx], EAX //线程1 MOV [g_lx], EAX //线程2
预计g_lx的值是2,但是结果却是1;这就是多个线程同时操作用一个资源时,可能会出现的问题。
================================================================
================================================================
1,如何解决原子方式控制变量,可以使用如下函数:
LONG InterlockedExchangeAdd( PLONG volatile PlAddend, //长整型变量地址 LONG lIncrement); //增长量,如果做减法,可以将此值改成负数。 LONG InterlockedExchangeAdd64( PLONGLONG volatile PlAddend, LONGLONG lIncrement); //函数的返回值是:返回*plAddend中原来的值
2,如果变量的增长值是1,也可以使用InterlockedIncrement函数。
如果变量的减少值是1,也可以使用InterlockedDecrement函数。
3,InterlockedExchange和InterlockedExchangePointer函数
LONG InterlockedExchange( PLONG volatile plTarget, //当前值得地址 LONG lValue); //替换值 PVOID InterlockedExchangePointer( PVOID* volatile ppvTarget, PVOID pvValue);
这两个函数式将 第二个参数替换第一个参数。应用此函数可以实现旋转锁。下为旋转锁实例:
// 全局变量标示这个资源是否正在被使用 BOOL g_fResourceInUse = FALSE; void Func1() { //等待访问这个资源,不停的假装告诉系统我正在使用这个资源 //但是,是通过InterlockedExchange这个原子函数告诉系统的, //这意味着如果这个资源正被别的线程使用的时候,CPU不会让这件事变为事实 //但是呢如果,没线程使用访问改变量的话,当前线程就得逞了。 //看来主动一些就会有收获的, //接下来这个非常主动的线程将要跳出辛苦的循环请求过程享受他的成果了。 while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE) Sleep(0); //访问刚占有的资源 //Access the resource. //We no longer need to access the resource. //我不用了,你拿去玩吧。 //其实可能有好多和他一样的进程在做一样的事 InterlockedExchange(&g_fResourceInUse, FALSE); }
使用旋转锁应该注意的地方:
(1)使用这种同步方式,我们需要保证需要同步的进程是运行在同一优先级的,如果两个线程优先级为4,另一个为3,那么优先级为4的线程不会得到等待资源的机会。
(2)应该避免资源标示和资源本身在同一高速缓存行(高速缓冲区:)
(3)应避免在单处理器的系统上使用旋转锁
4,用于交换的函数:
LONG InterlockedCompareExchange(//返回值:返回原目标值 LPLONG Destination, //对目标指针的值 LONG Exchange, //交换值(同目标值交换) LONG Comperand ); //比较值(同目标值比较) PVOID InterlockedCompareExchangePointer( PVOID *Destination, PVOID Exchange, PVOID Comperand );
5,单向链表函数
InitializeSListHead | 创建一个空栈 |
InterlockedPushEntrySList | 在栈顶添加一个元素 |
InterlockedPopEntrySList | 在栈顶移除一个元素并将它返回 |
InterlockedFlushSLIst | 清空栈 |
QueryDepthSList | 返回栈中元素的数量 |