windows 线程同步方式
参考
《windwos 核心编程》
http://baike.baidu.com/view/2808915.htm
1.事件(Event)
特点:适用与多进程,多线程,用户
2.互斥量(Mutex)
特点:多线程
互斥锁的功能和临界区域很相似。 区别是:Mutex所花费的时间比Critical Section多的多,但是Mutex是核心对象(Event、Semaphore也是),可以跨进程使用,而且等待一个被锁住的Mutex可以设定 TIMEOUT,不会像Critical Section那样无法得知临界区域的情况,而一直死等。 MFC中的对应类为CMutex。Win32函数有:创建互斥体CreateMutex() ,打开互斥体OpenMutex(), 释放互斥体ReleaseMutex()。 Mutex的拥有权并非属于那个产生它的线程,而是最后那个对此Mutex进行等待操作 (WaitForSingleObject等等)并且尚未进行ReleaseMutex()操作的线程。 线程拥有Mutex就好像进入Critical Section一样,一次只能有一个线程拥有该Mutex。 如果一个拥有Mutex的线程在返回之前没有调用ReleaseMutex(),那么这个 Mutex就被舍弃了, 但是当其他线程等待(WaitForSingleObject等)这个Mutex时,仍能返回,并得到一个 WAIT_ABANDONED_0返回值。能够知道一个Mutex被舍弃是Mutex特有的。
3.临界区(Critical Section)
特点:多线程、最轻量
临界区域的一个缺点就是:Critical Section不是一个核心对象,无法获知进入临界区的线程是生是死,如果进入临界区的线程挂了,没有释放临界资源,系统无法获知,而且没有办法释放该临界资源。
这个缺点在互斥器(Mutex)中得到了弥补。Critical Section在MFC中的相应实现类是CcriticalSection。CcriticalSection::Lock()进入临界区,CcriticalSection::UnLock()离开临界区。
4.信号量(Semaphores)
信号量是最具历史的同步机制。信号量是解决producer/consumer问题的关键要素。 对应的MFC类是Csemaphore。 Win32函数CreateSemaphore()用来产生信号量。 ReleaseSemaphore()用来解除锁定。 Semaphore的现值代表的意义是目前可用的资源数,如果Semaphore的现值为1,表示还 有一个锁定动作可以成功。如果现值为5,就表示还有五个锁定动作可以成功。 当调用Wait…等函数要求锁定,如果Semaphore现值不为 0,Wait…马上返回,资源数减1。当调用ReleaseSemaphore()资源数加1,当然不会超过初始设定的资源总数。
5.原子访问:Interlocked系列函数
使用场景:
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。
3、信号量:为控制一个具有有限数量用户资源而设计。
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
Windows下进程和线程同步的四种控制方法 http://blog.csdn.net/chainsmoker2010/article/details/6038678
Windows几种线程同步方法介绍 http://www.cnblogs.com/hlxs/archive/2012/12/30/2840031.html
InitializeCriticalSectionAndSpinCount、InitializeCriticalSection、InitializeCriticalSectionEx 介绍
InitializeCriticalSectionAndSpinCount 和InitializeCriticalSection(旧IF) 的区别是前者可以设置spin 次数,后者不行。
利用 InitializeCriticalSectionEx 也可以设置spin次数。(新IF)
主要的作用都是初始化一个临界区。
dwSpinCount [in]含义
The spin count for the critical section object. On single-processor systems, the spin count is ignored and the critical section spin count is set to 0 (zero). On multiprocessor systems, if the critical section is unavailable, the calling thread spin dwSpinCount times before performing a wait operation on a semaphore associated with the critical section. If the critical section becomes free during the spin operation, the calling thread avoids the wait operation.
相关系列的还有:SetCriticalSectionSpinCount,EnterCriticalSection, TryEnterCriticalSection, or LeaveCriticalSection
记得最后要DeleteCriticalSection
官方文档
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683476(v=vs.85).aspx
相关资料
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683477(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686908(v=vs.85).aspx
// Global variable CRITICAL_SECTION CriticalSection; int main( void ) { ... // Initialize the critical section one time only. if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x00000400) ) return; ... // Release resources used by the critical section object. DeleteCriticalSection(&CriticalSection); } DWORD WINAPI ThreadProc( LPVOID lpParameter ) { ... // Request ownership of the critical section. EnterCriticalSection(&CriticalSection); // Access the shared resource. // Release ownership of the critical section. LeaveCriticalSection(&CriticalSection); ... return 1; }
InitializeCriticalSectionAndSpinCount 介绍 http://blog.csdn.net/niitlcj/article/details/4996992
The InitializeCriticalSectionAndSpinCount function initializes a critical section object and sets the spin count for the critical section. BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, // pointer to critical section DWORD dwSpinCount // spin count for critical section ); SetCriticalSectionSpinCountThe SetCriticalSectionSpinCount function sets the spin count for the specified critical section. DWORD SetCriticalSectionSpinCount( LPCRITICAL_SECTION lpCriticalSection, // pointer to critical section DWORD dwSpinCount // spin count for critical section );
-
当线程试图进入另一个线程拥有的关键代码段时,调用线程就立即被置于等待状态。这意味着该线程必须从用户方式转入内核方式(大约1 0 0 0个C P U周期)。
这种转换是要付出很大代价的。 -
因此, InitializeCriticalSectionAndSpinCount 的作用不同于InitializeCriticalSection 之处就在于设置了一个循环锁,
不至于使线程立刻被置于等待状态而耗费大量的CPU周期,而在dwSpinCount后才转为内核方式进入等待状态。通常dwSpinCount设为4000较为合适 。 -
实际上对 CRITICAL_SECTION 的操作非常轻量,为什么还要加上旋转锁的动作呢?其实这个函数在单cpu的电脑上是不起作用的,
只有当电脑上存在不止一个cpu,或者一个cpu但多核的时候,才管用。 -
如果临界区用来保护的操作耗时非常短暂,比如就是保护一个reference counter,或者某一个flag,那么几个时钟周期以后就会离开临界区。
可是当这个thread还没有离开临界区之前,另外一个thread试图进入 此临界区——这种情况只会发生在多核或者smp的系统上——发现无法进入,
于是这个thread会进入睡眠,然后会发生一次上下文切换。我们知道context switch是一个比较耗时的操作,据说需要数千个时钟周期,
那么其实我们只要再等多几个时钟周期就能够进入临界区,现在却多了数千个时钟周期的开销,真 是是可忍孰不可忍。 -
所以就引入了InitializeCriticalSectionAndSpinCount函数,它的第一个参数是指向cs的指针,第二个参数 是旋转的次数。
我的理解就是一个循环次数,比如说N,那么就是说此时EnterCriticalSection()函数会内部循环判断此临界区是否可以进 入,直到可以进入或者N次满。
我们增加的开销是最多N次循环,我们可能获得的红利是数千个时钟周期。对于临界区内很短的操作来讲,这样做的好处是大大的。 -
MSDN上说,他们对于堆管理器使用了N=4000的旋转锁,然后“This gives great performance and scalability in almost all worst-case scenarios.” 可见还是很有用的:-)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)