多线程第七篇:互斥和同步总结
2013-12-16 13:46
141 查看
同步:多个线程(进程)之间有严格的先后顺序,一个线程(进程)的执行,依赖于另一个.
互斥:多个线程(进程)没有先后顺序,谁抢到算谁的.
注意:线程的创建和执行是分开的,并不是创建后立即执行,而是创建后,等待cpu调度.
window中的四种线程(进程)间同步与互斥.
criticalSection,mutex,event,semaphore
criticalSection:
1.中文名称临界区或者关键段,用于线程间互斥.
2.进入和离开必须成对出现.
3.不是内核对象,不能用于进程间互斥,只能用于线程间互斥
4.由于不是内核级对象,所以速度较快,效率较高.
使用:
1.InitializeCriticalSection
2.EnterCriticalSection
3.LeaveCriticalSection
注意事项:
临界区为子线程所有,主线程中使用临界区并不能实现互斥.
mutex:
1.互斥区,用于线程(进程)间互斥
2.记录拥有着,可以处理遗弃问题,当前使用该互斥区的线程(进程)占有该互斥区,并且,其他线程(进程)不能使用.
3.可以在线程A中waitforsignalobject,在线程B中release.
4.是内核对象,可以进行进程间互斥.
使用:
1.CreateMutex或者OpenMutex
2.ReleaseMutex
3.WaitForSingleObject
4.CloseHandle
event:
1.事件,用于线程(进程)间同步和互斥.
2.是内核对象,不具有拥有权,即可以由一个线程(进程)触发,而由另一个线程(进程)恢复.
使用:
1.CreateEvent
2.SetEvent
3.ResetEvent
4.PulseEvent
5.WaitForSingleObject
6.CloseHandle
semaphore:
1.信号量,用于线程(进程)间同步和互斥.
2.是内核对象,不具有拥有权,可以在不同的线程(进程)中进行PV操作.
使用:
1.CreateSemaphore 或OpenSemaphore
2.ReleaseSemaphore(+n)
3.WaitForSingleObject(-1)
4.CloseHandle
遗弃问题:
由于临界区是线程级别使用的,线程的操作都是由主线程控制的,当一个正在使用临界区的线程突然崩溃会怎么样呢?由于临界区会记录使用的线程,所以同样可以处理遗弃问题.
遗弃问题:
由于进程(线程)突然死亡,而没有进行任何善后处理,这样会造成资源永远被占用,需要一种方法来解决.
解决之道
当mutex使用者突然死了,那么mutex归零,等待其他使用者占有然后使用.
为什么event和semaphore不可以:
因为event和semaphore并不记录其使用者,他的使用者死了event和semaphore并不知道,他只知道自己被使用,等待被释放.
如果event和semaphore的使用者突然死亡,而其进程都在等待event和semaphore可用,那么这就造成了死锁.
为什么mutex和临界区(critical_section)不能进行同步:
因为mutex和critical不能再主线程中使用,而信号量和事件都可以在主线程中使用.
回忆一下,我们每次进行线程间同步的时候,总是先在主线程先设置事件或者信号量的状态,然后再在线程中设置,这样线程就可以使得线程有先后顺序了,即实现同步.核心在于主线程总是第一个执行,然后再执行子线程,这样就可以有先后顺序,如果不在主线程中设置,只是让子线程进行同步,也可以这需要一个全局变量,来进行检测是不是该这个线程执行.
我们用临界区+全局变量来实现一个线程同步:
[align=left][/align]
![](http://img.blog.csdn.net/20131217205548437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzAwOTU3NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
扩展:
可不可以让信号量和事件实现遗弃呢?
当然可以,我们可以在线程调用信号量或事件的时候,在内核对象中添加该进程号.在进程表或其他地方注册一个回调函数,此函数功能是当该进程或线程崩溃的时候,通知相关内核对象(信号量内核对象或者事件内核内核对象),此线程已崩溃,删除该线程相关信息.
此机制类似于epoll机制,有兴趣可以了解一下.
互斥:多个线程(进程)没有先后顺序,谁抢到算谁的.
注意:线程的创建和执行是分开的,并不是创建后立即执行,而是创建后,等待cpu调度.
window中的四种线程(进程)间同步与互斥.
criticalSection,mutex,event,semaphore
criticalSection:
1.中文名称临界区或者关键段,用于线程间互斥.
2.进入和离开必须成对出现.
3.不是内核对象,不能用于进程间互斥,只能用于线程间互斥
4.由于不是内核级对象,所以速度较快,效率较高.
使用:
1.InitializeCriticalSection
2.EnterCriticalSection
3.LeaveCriticalSection
注意事项:
临界区为子线程所有,主线程中使用临界区并不能实现互斥.
mutex:
1.互斥区,用于线程(进程)间互斥
2.记录拥有着,可以处理遗弃问题,当前使用该互斥区的线程(进程)占有该互斥区,并且,其他线程(进程)不能使用.
3.可以在线程A中waitforsignalobject,在线程B中release.
4.是内核对象,可以进行进程间互斥.
使用:
1.CreateMutex或者OpenMutex
2.ReleaseMutex
3.WaitForSingleObject
4.CloseHandle
event:
1.事件,用于线程(进程)间同步和互斥.
2.是内核对象,不具有拥有权,即可以由一个线程(进程)触发,而由另一个线程(进程)恢复.
使用:
1.CreateEvent
2.SetEvent
3.ResetEvent
4.PulseEvent
5.WaitForSingleObject
6.CloseHandle
semaphore:
1.信号量,用于线程(进程)间同步和互斥.
2.是内核对象,不具有拥有权,可以在不同的线程(进程)中进行PV操作.
使用:
1.CreateSemaphore 或OpenSemaphore
2.ReleaseSemaphore(+n)
3.WaitForSingleObject(-1)
4.CloseHandle
遗弃问题:
由于临界区是线程级别使用的,线程的操作都是由主线程控制的,当一个正在使用临界区的线程突然崩溃会怎么样呢?由于临界区会记录使用的线程,所以同样可以处理遗弃问题.
遗弃问题:
由于进程(线程)突然死亡,而没有进行任何善后处理,这样会造成资源永远被占用,需要一种方法来解决.
解决之道
当mutex使用者突然死了,那么mutex归零,等待其他使用者占有然后使用.
为什么event和semaphore不可以:
因为event和semaphore并不记录其使用者,他的使用者死了event和semaphore并不知道,他只知道自己被使用,等待被释放.
如果event和semaphore的使用者突然死亡,而其进程都在等待event和semaphore可用,那么这就造成了死锁.
为什么mutex和临界区(critical_section)不能进行同步:
因为mutex和critical不能再主线程中使用,而信号量和事件都可以在主线程中使用.
回忆一下,我们每次进行线程间同步的时候,总是先在主线程先设置事件或者信号量的状态,然后再在线程中设置,这样线程就可以使得线程有先后顺序了,即实现同步.核心在于主线程总是第一个执行,然后再执行子线程,这样就可以有先后顺序,如果不在主线程中设置,只是让子线程进行同步,也可以这需要一个全局变量,来进行检测是不是该这个线程执行.
我们用临界区+全局变量来实现一个线程同步:
#include <iostream> #include <windows.h> #include <process.h> int g_count = 0; CRITICAL_SECTION all; unsigned int __stdcall producer (void *) { int ID = 0; bool flage = true ; while ( flage ){ EnterCriticalSection (&all ); if ( g_count == ID ){ std ::cout << "producer"<< std ::endl ; flage = false ; g_count ++; } LeaveCriticalSection (&all ); } return 0; } unsigned int __stdcall consumer (void *) { int ID = 1; bool flage = true ; while ( flage ){ EnterCriticalSection (&all ); if ( g_count == ID ){ std ::cout << "consumer"<< std ::endl ; flage = false ; } LeaveCriticalSection (&all ); } return 0; } int main () { InitializeCriticalSection(& all ); HANDLE hproducer = (HANDLE ) _beginthreadex( NULL ,0,producer , NULL,0, NULL ); HANDLE hconsumer = (HANDLE ) _beginthreadex( NULL ,0,consumer , NULL,0, NULL ); WaitForSingleObject( hproducer ,INFINITE ); WaitForSingleObject( hconsumer ,INFINITE ); return 0; }
[align=left][/align]
扩展:
可不可以让信号量和事件实现遗弃呢?
当然可以,我们可以在线程调用信号量或事件的时候,在内核对象中添加该进程号.在进程表或其他地方注册一个回调函数,此函数功能是当该进程或线程崩溃的时候,通知相关内核对象(信号量内核对象或者事件内核内核对象),此线程已崩溃,删除该线程相关信息.
此机制类似于epoll机制,有兴趣可以了解一下.
相关文章推荐
- (转)Apple Push Notification Services in iOS 6 Tutorial: Part 1/2
- 哎呀
- 归并排序1
- RedHat安装oracle 10g系统环境配置脚本
- 面试10大算法汇总+常见题目解答
- Oracle 常用的几个命令在SQL*PLUS中
- linux之 查看文件命令cat,more,less,tail,tac,nl,od .
- php.ini 中开启短标签 <?=$?>
- jpeg
- [置顶] 每日震精图
- 几种简单的排序算法
- 机器学习资料总结
- Oracle查看用户表
- python3.2安装tornado
- js多个引力小球计算
- 电驴不能下载的办法
- Office 365系列(三) -Office 365 Pro plus 安装
- test9-3(学生信息类)
- UVa 10985 Rings'n'Ropes / floyd + dfs
- 远程桌面限制用户使用同一个会话