windows多线程同步互斥--总结

秒杀多线程面试题系列

参考JustDoIT —— 大部分内容

《Windows核心编程》线程同步对象速查表

对象
何时处于未触发状态 何时处于触发状态 WaitForSingleObject副作用 内核对象
自动重置事件 ResetEvent,PulseEvent或者等待成功 SetEvent,PulseEvent 重置通知
手动重置事件 ResetEvent,PulseEvent SetEvent,PulseEvent 没有
自动重置可等待计时器 CancelWaitableTimer或等待成功 SetWaitableTimer 重置计时器
手动重置可等待计时器 CancelWaitableTimer SetWaitableTimer 没有
信号量 等待成功时 计数大于0的时候(ReleaseSemaphore) 计数器减1
互斥量 等待成功时 不为线程占用时(ReleaseMutex) 把所有权交给线程
关键段 等待成功时((Try)EnterCriticalSection) 不为线程占用时(LeaveCriticalSection) 把所有权交给线程 ×
SRWLock 等待成功时(AcquireSRWLock(Exclusive)) 不为线程占用时(ReleaseSRWLock(Exclusive) 把所有权交给线程 ×
条件变量 等待成功(SleepConditionVariable*) 被唤醒时(Wake(All)ConditionVariable) 没有 ×

一 同步互斥区别

同步与互斥的区别和联系

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

 秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量

当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

(1).间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

(2).直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步。

二 同步互斥的方法

分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。

注:因此,效率上,临界区比互斥量等要高。

用户模式下的方法有:原子操作、临界区、读写锁,条件变量

内核模式下的方法有:互斥量、信号量、事件(手动和自动),可等待计时器(手动和自动)

三 临界区、互斥量、信号量、事件 对比

image

1、关于线程所有权属性:即某个线程获得该同步工具后,在他释放该工具前可以多次进入想访问的资源,一般来说具有所有权属性的工具不用于线程同步,只用于互斥,具体可以参考本文最前面的几篇博客

2、内核模式下的工具可以用于不同进程的线程之间的同步互斥,用户模式则只能用于相同进程的线程之间

3、只有互斥量在线程异常退出时,会释放对该工具的所有权,其他线程可以继续获取。其他的工具在线程异常退出时,其占有的工具不会释放,其他线程需要一直等待

4、事件分为自动和手动,如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。

四、死锁

百度百科

 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

一个典型的例子是:

线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象.

死锁可以用LockCop来检测,其源码地址为https://github.com/lattesir/WindowsViaCPP/tree/master/09-LockCop

注:该工具使用了Windows Vista/ 7提供的WCT API,故需要在Windows Vista/ 7系统运行LockCop检测工具。

windows 多线程编程的几点经验 (防止死锁)

1) 不要在线程函数体内操作MFC控件,不要再线程里面调用UpdateData函数更新用户界面,而应该尽量采用发送消息的方式,在主线程的消息响应函数中操作控件;

注:Winform/WPF中在线程中更新界面会抛出异常,解决办法也是用委托。

2)不建议采用SendMessage往主线程发送消息,因为它是同步的,阻塞的,可以考虑采用PostMessage代替;

3)线程退出时,尽量不要使用TerminateThread函数,而尽可能的让线程自己退出;

注:在线程中有malloc/new等操作,强行终止,1会导致内存泄露,2是因为整个进程在分配和回收内存时,都要用同一把锁,如果TerminateThread之后再new很有可能会死锁。CloseHandle(),TerminateThread(),ExitThread()的区别

4) 当线程退出时,必须先等待工作者线程退出,主线程才退出,但是在主线程里面不要使用WaitForSingleObject或WaitForMultiObjects等待线程结束,因为它可能造成死锁,当主线程使用这两个函数时,主线程就挂起了,尤其在第 (1), (2) 种情况下,工作者线程还在调用主线程里面的资源,这样造成死锁;

5) 为了防止退出死锁的发生,尽量使用MsgWaitForMultipleObjects函数,因为该函数等待时,可以等待线程句柄 有信号,而且还可以等待消息,不会造成死锁;

你可能感兴趣的:(windows多线程同步互斥--总结)