本系列意在记录Windwos线程的相关知识点,包括线程基础、线程调度、线程同步、TLS、线程池等
本篇介绍与内核对象同步相关的Event对象和Mutex对象
AutoResetEvent和ManualResetEvent
同步事件有两种:AutoResetEvent和 ManualResetEvent。主要用户线程之间的通知,实现同步。无论是哪种,实际上是每个Event对象关联了一个事件内核对象。在【Windows】线程漫谈——线程同步之等待函数和事件内核对象中,详细介绍了事件内核对象和等待函数。.NET这里的两个版本是对它们的封装。下面简单回顾一下事件内核对象:
事件内核对象有两种状态:触发状态、非触发状态。等待函数用来检测某种内核对象的状态,当锁检测的内核对象是触发状态的时候等待函数将直接返回,否则将阻塞。(这样的表述其实是不完整的,这里只是简单描述了一般的场景)。线程可以通过Set方法使事件对象变为触发状态,这样如果有一个线程正在等待这个对象触发,那么相当于它将收到“事件通知”而返回。AutoReset的事件内核对象在被设置为触发态后将自动回到非触发态,ManualReset的事件对象不会自动重置为未触发状态。
.NET的封装相应的有两个版本,自动重置和手动重置,他们实际上是一样的对象,只是行为稍有不同而已。在初始化事件对象的时候可以指定事件对象的初始化状态:
static AutoResetEvent myResetEvent = new AutoResetEvent(false);//初始化为非触发状态
调用Set方法将事件对象置为触发状态:
event_1.Set();
调用Reset方法将事件对象置为非触发状态:
event_1.Reset();
调用WaitOne、WaitAny或WaitAll来使线程等待事件对象,它们的用法可以顾名思义。
下面对比一下.NET封装的事件对象和windows原生的事件对象,关于windows原生的事件内核对象参见【Windows】线程漫谈——线程同步之等待函数和事件内核对象
.NET | Windows API | 说明 |
AutoResetEvent | CreateEvent(bManualReset=false) | |
ManualResetEvent | CreateEvent(bManualReset=true) | |
.Set | SetEvent | |
.Reset | ResetEvent | |
AutoResetEvent | PulseEvent | .NET没提供PulseEvent,对于事件对象而言,用AutoResetEvent代替 |
-- | Named | .NET下事件对象似乎无法命名,也就是说事件对象不能跨进程。但是Windows原生的事件对象是跟其他内核对象一样是可以命名的,因此可以跨进程共享 |
EventWaitHandle.WaitOne | WaitForSingleObject | .NET通过在基类EventWaitHandle中定义Wait系列函数,封装了等待函数 |
EventWaitHandle.WaitAll | WaitForMultipleObjects (bWaitAll=true) |
|
EventWaitHandle.WaitAny | WaitForMultipleObjects (bWaitAll=false) |
Mutex
.NET下单Mutex和Win32下单Mutex就是同一个东西,所以,完全可以从Win32的互斥量内核对象来理解。跟通常的内核对象一样,互斥量具有触发和未触发两种。当对象为触发状态的时候,等待他的线程将获得其访问权,并置为未触发状态;反之,对象为未触发状态时,等待他的线程将挂起。与Monitor和lock相同,Mutex也有记录所属线程ID,使用计数和递归计数,因此,同一个线程可以多次获得同一个Mutex的访问权限,但是要保证能够有相同次数的释放。
通过构造函数创建Mutex对象
private static Mutex mut = new Mutex();
除了不带参数的构造函数,还有一些可选的参数用于创建Mutex对象:可以初始化创建的线程是否立刻拥有Mutex的访问权限、可以为Mutex命名,命名的Mutex可以对其他进程可见,而不仅仅是本进程的线程。关于命名MSDN还有如下的说明:
在运行终端服务的服务器上,已命名的系统 mutex 可以具有两级可见性。 如果名称以前缀“Global\”开头,则 mutex 在所有终端服务器会话中均为可见。 如果名称以前缀“Local\”开头,则 mutex 仅在创建它的终端服务器会话中可见。 在这种情况下,服务器上各个其他终端服务器会话中都可以拥有一个名称相同的独立 mutex。 如果创建已命名 mutex 时不指定前缀,则它将采用前缀“Local\”。 在终端服务器会话中,只是名称前缀不同的两个 mutex 是独立的 mutex,这两个 mutex 对于终端服务器会话中的所有进程均为可见。 即:前缀名称“Global\”和“Local\”说明 mutex 名称相对于终端服务器会话(而并非相对于进程)的范围。
通过Mutex.Wait系列函数尝试获得对Mutex的访问权限
mut.WaitOne();
通过Mutex.ReleaseMutex释放Mutex
mut.ReleaseMutex();
在我之前的文章【Windows】线程漫谈——线程同步之信号量和互斥量中详细阐述了Win32下互斥量对象,下面对两种API作一个比较
.NET | Windows API | 说明 |
Mutex | CreateMutex | |
Mutex | OpenMutex | |
.ReleaseMutex | ReleaseMutex | |
Mutex(bool,string) | Named | .NET下可以对Mutex对象命名 |
EventWaitHandle.WaitOne | WaitForSingleObject | .NET通过在基类EventWaitHandle中定义Wait系列函数,封装了等待函数 |
EventWaitHandle.WaitAll | WaitForMultipleObjects (bWaitAll=true) |
|
EventWaitHandle.WaitAny | WaitForMultipleObjects (bWaitAll=false) |
劳动果实,转载请注明出处:http://www.cnblogs.com/P_Chou/archive/2012/08/19/event-and-mutex-in-net-thread-sync.html