WaitHandle详解
一、类的定义
public abstract partial class WaitHandle : MarshalByRefObject, IDisposable
二、WaitHandle类中的字段
internal const int MaxWaitHandles = 64;
protected static readonly IntPtr InvalidHandle = new IntPtr(-1);
//SafeWaitHandle继承了SafeHandleZeroOrMinusOneIsInvalid,重写了ReleaseHandle方法,
//SafeWaitHandle将handle保存到了SafeHandle中,因此在WaitHandle中可以安全释放Handle
private SafeWaitHandle? _waitHandle;
[ThreadStatic]
private static SafeWaitHandle?[]? t_safeWaitHandlesForRent;
//成功等到返回值,该值与Win32中的WAIT_OBJECT_0相对应;
//当等待多个对象返回时,返回值为WaitSuccess + waitIndex
internal const int WaitSuccess = 0;
//对应Win32中的WAIT_ABANDONED。指定的object互斥的,在当前拥有该对象的线程终止前,
//该对象不会被原来的线程释放。 当等待多个对象返回时,返回值为WaitAbandoned + waitIndex。
internal const int WaitAbandoned = 0x80;
//对应Win32中的WAIT_TIMEOUT
public const int WaitTimeout = 0x102;
public virtual IntPtr Handle {
get => _waitHandle == null ? InvalidHandle : _waitHandle.DangerousGetHandle();
set{
if (value == InvalidHandle){
// This line leaks a handle. However, it's currently
// not perfectly clear what the right behavior is here
// anyways. This preserves Everett behavior. We should
// ideally do these things:
// *) Expose a settable SafeHandle property on WaitHandle.
// *) Expose a settable OwnsHandle property on SafeHandle.
if (_waitHandle != null){
_waitHandle.SetHandleAsInvalid();
_waitHandle = null;
}
}else{
_waitHandle = new SafeWaitHandle(value, true);
}//if...else...
}//set
}//Handle
[AllowNull]
public SafeWaitHandle SafeWaitHandle{
get => _waitHandle ??= new SafeWaitHandle(InvalidHandle, false);
set => _waitHandle = value;
}
三、WaitHandle的构造函数
只有一个空参构造函数
protected WaitHandle(){}
四、WaitHandle中的方法
1、WaitOne方法
public virtual bool WaitOne(int millisecondsTimeout){
if (millisecondsTimeout < -1){
//如果超时时间不合法直接抛出异常
throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}
//程序运行到这里说明超时时间合法
return WaitOneNoCheck(millisecondsTimeout);
}
private bool WaitOneNoCheck(int millisecondsTimeout)
{
//确认超时时间合法
Debug.Assert(millisecondsTimeout >= -1);
//如果SafeWaitHandle为空则抛出异常
SafeWaitHandle waitHandle = _waitHandle ?? throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic);
bool success = false;
try {
int waitResult;
//调用SafeHandle中的DangerousAddRef
waitHandle.DangerousAddRef(ref success);
SynchronizationContext? context = SynchronizationContext.Current;
if (context != null && context.IsWaitNotificationRequired()) {
//如果存在SynchronzationContext,则调用context的wait方法
waitResult = context.Wait(new[] { waitHandle.DangerousGetHandle() }, false, millisecondsTimeout);
} else{
//如果不存在SynchronzationContext,则调用WaitOneCore方法;DangerousGetHandle时SafeHandle中返回handle方法
waitResult = WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout);
}
if (waitResult == WaitAbandoned){
//如果WaitAbandoned则抛出异常
throw new AbandonedMutexException();
}
//如果没有超时,则返回true
return waitResult != WaitTimeout;
}finally{
if (success){//如果AddRef成功,则在这里进行Release
waitHandle.DangerousRelease();
}
}//try...finally...
}
1.1、WaitOneCore方法
该方法是一个外部方法(extern),声明如下:
private static extern int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout);
2、WaitAll和WaitAny方法
从下WaitAll和WaitAny的方法定义中,我们可以看到,他们最终都是通过WaitMultiple(ReadOnlySpan waitHandles, bool waitAll, int millisecondsTimeout)方法实现的,他们的区别在于出入的waitAll参数的值不同
public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout) =>
WaitMultiple(waitHandles, true, millisecondsTimeout) != WaitTimeout;
public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) =>
WaitMultiple(waitHandles, false, millisecondsTimeout);
2.1、 WaitMultiple方法
private static int WaitMultiple(ReadOnlySpan<WaitHandle> waitHandles, bool waitAll, int millisecondsTimeout){
if (waitHandles.Length == 0){
throw new ArgumentException(SR.Argument_EmptyWaithandleArray, nameof(waitHandles));
}
if (waitHandles.Length > MaxWaitHandles){
throw new NotSupportedException(SR.NotSupported_MaxWaitHandles);
}
if (millisecondsTimeout < -1){
throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}
SynchronizationContext? context = SynchronizationContext.Current;
bool useWaitContext = context != null && context.IsWaitNotificationRequired();
//获取一个数组用于存放SafeWaitHandler,此时数组中没有添加任何的元素
SafeWaitHandle?[]? safeWaitHandles = RentSafeWaitHandleArray(waitHandles.Length);
try{
int waitResult;
if (useWaitContext){
IntPtr[] unsafeWaitHandles = new IntPtr[waitHandles.Length];
//遍历waitHandles,将waitHandles中的每个元素对应的handle放到safeWaitHandles中,
//对应safeWaitHandle中封装的handle存放到unsafeWaitHandles中
ObtainSafeWaitHandles(waitHandles, safeWaitHandles, unsafeWaitHandles);
waitResult = context!.Wait(unsafeWaitHandles, waitAll, millisecondsTimeout);
}else{
Span<IntPtr> unsafeWaitHandles = stackalloc IntPtr[waitHandles.Length];
ObtainSafeWaitHandles(waitHandles, safeWaitHandles, unsafeWaitHandles);
waitResult = WaitMultipleIgnoringSyncContext(unsafeWaitHandles, waitAll, millisecondsTimeout);
}
if (waitResult >= WaitAbandoned && waitResult < WaitAbandoned + waitHandles.Length){
if (waitAll){
//如果waitAll==true,则直接抛出异常,因为系统不会告诉我们是哪一个出错了
throw new AbandonedMutexException();
}
//waitResult减去WaitAbandoned的结果就是出错的handle的位置序号,所以这里可以具体指出
//是哪一个handle出错了
waitResult -= WaitAbandoned;
throw new AbandonedMutexException(waitResult, waitHandles[waitResult]);
}
//返回执行结果
return waitResult;
}finally{
for (int i = 0; i < waitHandles.Length; ++i){
//realse每一个safeWaitHandle
if (safeWaitHandles[i] is SafeWaitHandle swh){
swh.DangerousRelease();
safeWaitHandles[i] = null;
}
}
//归还租借到的SafeWaitHandler数组
ReturnSafeWaitHandleArray(safeWaitHandles);
}
}
五、注意点
1、我们在WaitOne中调用的WaitOneNoCheck方法里面可以看到这样的一个if分支语句
if (context != null && context.IsWaitNotificationRequired()) {
//如果存在SynchronzationContext,则调用context的wait方法
waitResult = context.Wait(new[] { waitHandle.DangerousGetHandle() }, false, millisecondsTimeout);
} else{
//如果不存在SynchronzationContext,则调用WaitOneCore方法;DangerousGetHandle时SafeHandle中返回handle方法
waitResult = WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout);
}
另外,我们在WaitAny或者WaitAll中调用的WaitMultiple方法里面可以看到这样的if分支语句
bool useWaitContext = context != null && context.IsWaitNotificationRequired();
//获取一个数组用于存放SafeWaitHandler,此时数组中没有添加任何的元素
SafeWaitHandle?[]? safeWaitHandles = RentSafeWaitHandleArray(waitHandles.Length);
try{
int waitResult;
if (useWaitContext){
IntPtr[] unsafeWaitHandles = new IntPtr[waitHandles.Length];
//遍历waitHandles,将waitHandles中的每个元素对应的handle放到safeWaitHandles中,
//对应safeWaitHandle中封装的handle存放到unsafeWaitHandles中
ObtainSafeWaitHandles(waitHandles, safeWaitHandles, unsafeWaitHandles);
waitResult = context!.Wait(unsafeWaitHandles, waitAll, millisecondsTimeout);
}else{
Span<IntPtr> unsafeWaitHandles = stackalloc IntPtr[waitHandles.Length];
ObtainSafeWaitHandles(waitHandles, safeWaitHandles, unsafeWaitHandles);
waitResult = WaitMultipleIgnoringSyncContext(unsafeWaitHandles, waitAll, millisecondsTimeout);
}
}//try
通过对比可以发现,如果使用了SynchronzationContext则会在context上调用Wait。
2、所有的waitXX如果使用了SynchronzationContext,则最后都是通过调用WaitMultipleIgnoringSyncContext方法完成的
internal static unsafe int WaitMultipleIgnoringSyncContext(Span<IntPtr> waitHandles, bool waitAll, int millisecondsTimeout){
//fixed关键字使得声明的变量在内存的地址不会被垃圾回收器改变
fixed (IntPtr* pWaitHandles = &MemoryMarshal.GetReference(waitHandles)){
//调用有外部实现的方法
return WaitMultipleIgnoringSyncContext(pWaitHandles, waitHandles.Length, waitAll, millisecondsTimeout);
}
}
//该方法是一个外部方法
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe int WaitMultipleIgnoringSyncContext(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout);
private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout){
//通过调用本地方法来完成
int ret = SignalAndWaitNative(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout);
if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) {
//入宫运行出错则抛出异常
throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts);
}
//成功完成运行,返回结果值
return ret;
}
[MethodImpl(MethodImplOptions.InternalCall)]
//这是一个外部方法
private static extern int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout);