锁操做:为了确保操做区域的安全。通常有旋转锁、读写锁、原子操做。数组
原子操做:原子操做是指不会被线程调度机制打断的操做;这种操做一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另外一个线程)。
原子操做是不可分割的,在执行完毕以前不会被任何其它任务或事件中断。安全
例子:++i是否属于原子操做
答:不是原子操做
解析:
i++分为三个阶段:
1.内存到寄存器
2.寄存器自增
3.写回内存
这三个阶段中间均可以被中断分离开,因此不属于原子操做。数据结构
当咱们利用线程须要对一个全局变量进行累加、减操做时,怕被中断,可用原子操做。函数
int g_nCount = 0; spa
InterlockedExchangeAdd((long*)&g_nCount, 3);
对全局变量&g_nCount每次进行+3操做。线程
==InterlockedExchangeSubtract((long*)&g_nCount, 2)
InterlockedExchangeAdd((long*)&g_nCount, -2);
此两函数等价。可是InterlockedExchangeSubtract()最终也会调用InterlockedExchangeAdd()此函数。所以能够直接+(-2)。指针
InterlockedIncrement((long*)&g_nCount);
//对变量每次进行+1操做
InterlockedDecrement((long*)&g_nCount);
//对变量每次进行-1操做
这两个函数都是固定+(-)1,不能够自拟。code
旋转锁(Spin Lock):旋转锁是一种非阻塞锁,由某个线程独占。采用旋转锁时,等待线程并不静态地阻塞在同步点,而是必须“旋转”,
不断尝试直到最终得到该锁。blog
适用:
旋转锁多用于多处理器系统中。这是由于,若是在单核处理器中采用旋转锁,当一个线程正在“旋转”时,将没有执行资源可供另外一释放锁的线程使用。
旋转锁适合于任何锁持有时间少于将一个线程阻塞和唤醒所需时间的场合。线程控制的变动,包括线程上下文的切换和线程数据结构的更新。队列
危害:
在线程调用其余子系统时,线程不该持有旋转锁。对旋转锁的不当使用可能会致使线程饿死,所以需谨慎使用这种锁机制。旋转锁致使的饿死问题可以使用排队技术来解决,
即每一个等待线程按照先进先出的顺序或者队列结构在一个独立的局部标识上进行旋转
好比:进屋子,若是门开着的,就能够直接进入房子同时将反锁,不容许其余人进行。当本身离开房子时,再将门打开,等待下一我的。
函数解析:
InterlockedExchange((long *)&g_bIsUsing, true)
原型:
LONG InterlockedExchange(
_Inout_ LONG volatile *Target,
_In_ LONG Value
);
InterlockedExchange(a,b)能以原子操做的方式交换俩个参数a, b,并返回a之前的值;由于InterlockedExchange 是原子函数,不会要求停止中断,
因此交换指针的方式是安全的。
问题:传递线程参数时,为何保存在数组中,而不是直接传递i?
解析:
在循环中,咱们建立线程并传递的参数是i=1后,主程序有可能在执到下一次循环时,第一次的ThreadProc函数仍未执行,而此时的i已经=2了,若是ThreadProc再来调用
int nThreadNo = *(int*)lParam;语句时,显然不是咱们想要的结果。
解决方法:
可使用静态数组来保存所要传递的参数。此时,全部参数均保存在threadId数组中。
源代码: #include "stdafx.h" #include <Windows.h> #include <process.h> #define THREADNUM 10 int g_nCount = 0; bool g_bIsUsing = false; unsigned _stdcall ThreadProc(void *lParam) { int nThreadNo = *(int*)lParam; //等待访问共享资源 while (InterlockedExchange((long *)&g_bIsUsing, true) == true) Sleep(0); //访问共享资源 printf("线程%d开始执行\n", nThreadNo); printf("线程%d结束执行\n", nThreadNo); //结束访问 InterlockedExchange((long *)&g_bIsUsing, false); //原子操做 //InterlockedExchangeAdd((long*)&g_nCount, 3); //InterlockedExchangeAdd((long*)&g_nCount, -2); //InterlockedDecrement((long*)&g_nCount); //InterlockedIncrement((long*)&g_nCount); return 0; } int _tmain(int argc, _TCHAR* argv[]) { HANDLE pThreads[THREADNUM]; //保存当前执行线程的ID int threadId[THREADNUM]; //启动线程 for (int i = 0; i < THREADNUM; ++i) { threadId[i] = i; pThreads[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, threadId+i, 0, 0); if (pThreads[i] == 0) { continue; i--; } //休眠,可保证上一线程执行完成。即按序号执行 //Sleep(100); } WaitForMultipleObjects(THREADNUM, pThreads, TRUE, INFINITE); //经过原子操做,最后累加结果 //printf("g_nCount:%d", g_nCount); //释放资源 for (int i = 0; i < THREADNUM; ++i) { CloseHandle(pThreads[i]); } getchar(); return 0; }