3

WaitHandle.WaitAnyを呼び出すと、指定されたWaitHandle[]のコピーが割り当てられることに気づきました。以下のリンクまたはリフレクターを使用して見ることができるように:

http://reflector.webtropy.com/default.aspx/DotNET/DotNET/8@0/untmp/whidbey/REDBITS/ndp/clr/src/BCL/System/Threading/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の独自のコピーを作成します)?そして、おそらくなぜですか?

これは、私のシステムに多くの不要なメモリ割り当てがあることを意味します。低レベルの方法のため、これを複数のWaitHandlesで使用します。

トピックにとどまり、タスク並列ライブラリまたは同様のものへの参照を控えてください;)ここでは、GC圧力が重要な高性能シナリオについて話しています。

4

1 に答える 1

4

ガベージコレクションされていないWaitMultipleことを期待できる必要があります。WaitHandleそれが発生した場合、メモリの破損、または同様の厄介さのために、アクセス違反が発生する可能性があります。

アイデアは、失敗することなくWaitMultiple1つ以上のWaitHandleオブジェクトを呼び出して破棄できるようにする必要があるということです。WaitAnyコピーが作成されなかった場合、これは不可能であり、その特定のシナリオをデバッグするのに1日かかります。つまり、最終的には、スレッドセーフのために行われるということです。

基礎となるネイティブ関数WaitForMultipleObjectsのドキュメントを見ると、これの証拠があります。動作は未定義として記述されています。

待機がまだ保留中にこれらのハンドルの1つが閉じられた場合、関数の動作は未定義です。

以下に示すように、可能な限りすべてのパフォーマンスを引き出すことが重要な場合は、WaitHandlesが破棄されないようにし、WaitForMultipleObjectsをap/invoke呼び出しすることができます。WaitHandle.SafeWaitHandle問題の同期オブジェクトへのハンドルとして指定できます。

編集:上記の答えは間違っています。それが私を悩ませたので、私は時々この質問に戻ってきました。私は今、正しい答えがあると信じています。

この要素の転送の目的は、個々WaitHandleののスレッドセーフな検証です。開発者が元の配列を使用する場合、その要素の1つが値などで上書きされる可能性があり、そのnull結果、基になるネイティブ関数で未定義の動作が発生します。要素を内部配列にコピーすることで、各要素をチェックし、nullそれが無効であるか無効である場合は例外をスローして、それを格納できます。内部配列の要素は置き換えられないことがわかっています。したがって、以前の目的では、WaitHandle配列にnullまたはcross-AppDomain要素を配置するなどの奇妙なことをしていなくても問題ありません。

于 2013-03-12T17:18:17.417 に答える