我注意到对WaitHandle.WaitAny的调用会分配给它的WaitHandle []的副本。可以在下面的链接中看到或使用反射器:
相关代码是:
[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副本)?也许为什么不呢?
这意味着我的系统中存在大量不必要的内存分配。由于低级方式我们使用多个WaitHandles。
请保持主题并避免引用任务并行库或类似的东西;)我们正在谈论GC压力很重要的高性能场景。
答案 0 :(得分:4)
WaitMultiple
需要能够指望WaitHandle
没有被垃圾收集。如果发生这种情况,由于内存损坏或类似的恶意,它可能最终会导致访问冲突。
我们的想法是,您应该能够调用WaitMultiple
并销毁一个或多个WaitHandle
个对象而不会WaitAny
失败。如果它没有创建副本,这将是不可能的,并且调试 特定场景会花费你一整天。所以最重要的是它是为线程安全做的。
如果您查看基础本机函数的文档WaitForMultipleObjects,就会有这样的证据:行为被描述为未定义:
如果其中一个句柄在等待仍处于暂挂状态时关闭,则该函数的行为未定义。
如下所示,如果从中挤出所有性能很重要,则可以确保不处理WaitHandles,并对WaitForMultipleObjects进行p / invoke调用。您可以提供WaitHandle.SafeWaitHandle
作为相关同步对象的句柄。
编辑:上面给出的答案是错误的。我不时回到这个问题因为它困扰了我;我现在相信我有一个正确的答案。
这种元素转移的目的是对单个WaitHandle
进行线程安全验证。如果开发人员要使用原始数组,则可能会使用null
值覆盖其中一个元素,这将导致底层本机函数中的未定义行为。通过将元素复制到内部数组中,我们可以检查每个元素,如果它是null
或者其他方法无效则抛出异常,然后存储它。我们知道内部数组的元素无法替换。因此,对于您很久以前的目的,如果您没有做奇怪的事情,例如将null或跨AppDomain元素放入WaitHandle数组中,那么就没问题了。