2

たとえば、次のコードがあります。

IPrincipal capturedPrincipal = Thread.CurrentPrincipal;
myseq.AsParallel().Select(x =>
{
    Thread.CurrenctPrincipal = capturedPrincipal;
    /*call code protected with CAS*/
});

デリゲートが実行されるすべてのスレッドに確実Thread.CurrenctPrincipalに伝播されます。適切に設定Selectすれば、これは自動的に行われることに気づきました。SynchronizationContextでは、 PLINQはSynchronizationContext作業項目をキューに入れるときに使用しThreadPoolますか?いいえの場合、なぜですか?

ps

上記のコードは、IIS / WAS(ASP.NET互換性なし)でホストされているWCF環境で実行されることに注意することが重要だと思います。

編集:私が目撃した同じ行動を確認する同様の質問を 見つけました。

Edit2: casperOneのテストを少し変更しましたが、threadidが同じであると言って失敗します:

    [Test]
    public void Test1()
    {
        var principal = new GenericPrincipal(new GenericIdentity("test"), new string[0]);
        Thread.CurrentPrincipal = principal;
        int threadID = Thread.CurrentThread.ManagedThreadId;

        Enumerable.Range(0, 4000).AsParallel()
            .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            .Select(x =>
                {
                    Assert.AreSame(Thread.CurrentPrincipal, principal);
                    Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadID);
                    return x;
                })
            .ToArray();
    }
4

1 に答える 1

3

ここに2つの質問があります。1つ目は、 PLINQThread.CurrentPrincipalのスレッドに伝播されるかどうかです。

ExecutionContextこれはの一部であり、ExecutionContextは呼び出し元のスレッドからキャプチャされ、新しいスレッド/タスク/スレッドプールスレッドが開始されるときに新しい/リサイクルされたスレッドにコピーされるため、答えは「はい」です。

次のテストケース(.NET 4.0で実行)はこれを示しています。

[TestMethod]
public void TestMethod1()
{
    // Capture the current logged in account.
    // Could be a GenericPrincipal as well with some random value
    // set on the identity name.
    IPrincipal p = new WindowsPrincipal(WindowsIdentity.GetCurrent());

    // Set the current principal.
    Thread.CurrentPrincipal = p;

    // Set the synchronization context.
    SynchronizationContext.SetSynchronizationContext(
        new SynchronizationContext());

    // Context is not null.
    Assert.IsNotNull(SynchronizationContext.Current);

    // PLINQ.
    var plinqThreadDetails = 
        // Go parallel.  This number needs to be reasonably
        // high to force parallelization as PLINQ might
        // use this thread if the size is small.
        from i in Enumerable.Range(0, 4000).AsParallel().
            // Force parallelization.  At best, this is
            // a suggestion.
            WithExecutionMode(ParallelExecutionMode.ForceParallelism)
        select new {
            // These values are retreived on another thread.
            IdentityName = Thread.CurrentPrincipal.Identity.Name,
            Thread.CurrentThread.ManagedThreadId,
        };

    // Was there any parallelization?
    bool anyParallel = false;

    // Make assertions.
    // The managed thread id is different than the current one.
    foreach (var plinqThreadDetail in plinqThreadDetails)
    { 
        // But the principal still flowed, even though on a different
        // thread.
        Assert.AreEqual(Thread.CurrentPrincipal.Identity.Name,
            plinqThreadDetail.IdentityName);

        // Update any parallel.
        anyParallel |= (plinqThreadDetail.ManagedThreadId !=
            Thread.CurrentThread.ManagedThreadId);
    }

    // There was *some* parallelization.
    Assert.IsTrue(anyParallel);
}

PLINQで使用されているかどうかについてSynchronizationContextは、使用されておらず、意味がありません。

SynchronizationContext通常、特定のコンテキスト(通常はスレッドであり、UIアプリケーションを考えますが、ASP.NET同期コンテキストを考えると常にではありません)への呼び出しをシリアル化することを意味することを考えると、PLINQが並列化から得る利益はすべて無効になります。すべての呼び出しは、を介してマーシャリングする必要がありSynchronizationContextます。

PLINQの利点は、これらの操作を一度に1つずつではなく、同時に実行できることです。

SynchronizationContext次のテストケース(前のテストケースとほぼ同じ)は、がPLINQスレッドでキャプチャされていないことを証明しています。

[TestMethod]
public void TestMethod2()
{
    // Set the synchronization context.
    SynchronizationContext.SetSynchronizationContext(
        new SynchronizationContext());

    // Context is not null.
    Assert.IsNotNull(SynchronizationContext.Current);

    // PLINQ.
    var plinqThreadDetails = 
        // Go parallel.  This number needs to be reasonably
        // high to force parallelization as PLINQ might
        // use this thread if the size is small.
        from i in Enumerable.Range(0, 4000).AsParallel().
            // Force parallelization.
            WithExecutionMode(ParallelExecutionMode.ForceParallelism)
        select new {
            // These values are retreived on another thread.
            SynchronizationContextIsNull =
                SynchronizationContext.Current == null,
            Thread.CurrentThread.ManagedThreadId,
        };

    // Make assertions.
    // Was there any parallelization?
    bool anyParallel = false;

    // Make assertions.
    // The synchronization context on the PLINQ thread was
    // not set, only if on a different thread.
    foreach (var plinqThreadDetail in plinqThreadDetails)
    {
        // If the thread id is different.
        if (plinqThreadDetail.ManagedThreadId !=
            Thread.CurrentThread.ManagedThreadId)
        {
            // The synchronization context was null.
            Assert.IsTrue(plinqThreadDetail.SynchronizationContextIsNull);

            // There was something on another thread.
            anyParallel = true;
        }
        else
        {
            // The synchronization context is not null.
            Assert.IsFalse(plinqThreadDetail.SynchronizationContextIsNull);
        }
    }

    // There was *some* parallelization.
    Assert.IsTrue(anyParallel);
}
于 2012-10-24T12:32:26.800 に答える