C# WaitHandle

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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值