您的位置:首页 > 其它

多线程第七篇:互斥和同步总结

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不能再主线程中使用,而信号量和事件都可以在主线程中使用.
回忆一下,我们每次进行线程间同步的时候,总是先在主线程先设置事件或者信号量的状态,然后再在线程中设置,这样线程就可以使得线程有先后顺序了,即实现同步.核心在于主线程总是第一个执行,然后再执行子线程,这样就可以有先后顺序,如果不在主线程中设置,只是让子线程进行同步,也可以这需要一个全局变量,来进行检测是不是该这个线程执行.

我们用临界区+全局变量来实现一个线程同步:
#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机制,有兴趣可以了解一下.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: