WaitHandle.WaitAny每次被调用时都会分配WaitHandle[]的副本
本文关键字:WaitHandle 分配 副本 WaitAny 调用 | 更新日期: 2023-09-27 18:03:28
我一直注意到对WaitHandle的调用。WaitAny分配给它的WaitHandle[]的副本。如下面的链接所示,或者使用reflector:
http://reflector.webtropy.com/default.aspx/DotNET/DotNET/8@0 untmp/whidbey/REDBITS/民族/clr/src/BCL/系统/线程/WaitHandle@cs/3/WaitHandle@cs
相关代码为:
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
if (waitHandles==null)
{
throw new ArgumentNullException("waitHandles");
}
if (MAX_WAITHANDLES < waitHandles.Length)
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles"));
}
if (-1 > millisecondsTimeout)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
}
WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length];
for (int i = 0; i < waitHandles.Length; i ++)
{
WaitHandle waitHandle = waitHandles[i];
if (waitHandle == null)
throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_ArrayElement"));
if (RemotingServices.IsTransparentProxy(waitHandle))
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
internalWaitHandles[i] = waitHandle;
}
#if _DEBUG
// make sure we do not use waitHandles any more.
waitHandles = null;
#endif
int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, false /* waitany*/ );
for (int i = 0; i < internalWaitHandles.Length; i ++)
{
GC.KeepAlive (internalWaitHandles[i]);
}
if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret))
{
int mutexIndex = ret -WAIT_ABANDONED;
if(0 <= mutexIndex && mutexIndex < internalWaitHandles.Length)
{
throw new AbandonedMutexException(mutexIndex,internalWaitHandles[mutexIndex]);
}
else
{
throw new AbandonedMutexException();
}
}
else
return ret;
}
现在我的问题是为什么?这是否可以规避(即编写自己的WaitHandle.WaitAny副本)?为什么不呢?
这意味着在我的系统中有很多不必要的内存分配。由于低级的方式,我们使用了多个waithandle。
请保持主题,避免引用任务并行库或类似的;)我们在这里讨论的是一个高性能的场景,其中GC压力很重要。
WaitMultiple
需要能够指望WaitHandle
不被垃圾收集。如果发生这种情况,它可能会因为内存损坏或其他类似的问题而导致访问冲突。
这个想法是,您应该能够调用WaitMultiple
并销毁一个或多个WaitHandle
对象,而不会导致WaitAny
失败。如果它不创建副本,这将是不可能的,并且调试特定场景将花费您一整天的时间。所以底线是,这样做是为了线程安全。
如果您看一下底层本机函数WaitForMultipleObjects的文档,就会发现这样的证据:该行为被描述为未定义:
如果这些句柄中的一个在等待还未挂起时被关闭,则该函数的行为是未定义的。
如下所示,如果重要的是要从它中挤出所有性能,您可以确保不处置WaitHandles,并对WaitForMultipleObjects进行p/invoke调用。您可以提供WaitHandle.SafeWaitHandle
作为同步对象的句柄。
EDIT:上面的答案是错误的。我不时地回到这个问题上,因为它困扰着我;我现在相信我有一个正确的答案。
元素转移的目的是对单个WaitHandle
进行线程安全验证。如果开发人员要使用原始数组,则可能会使用null
值覆盖其中的一个元素,这将导致底层本机函数中的未定义行为。通过将元素复制到内部数组中,我们可以检查每个元素,如果它是null
或其他无效则抛出异常,然后存储它。我们知道内部数组的元素不能被替换。因此,对于您很久以前的目的,如果您不做奇怪的事情,如将null或跨appdomain元素放入WaitHandle数组中,您就可以了。