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 下托管的 WCF 环境中执行的(不兼容 ASP.NET)。

编辑: 发现了一个类似的问题,证实了我目睹的相同行为。

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

这里有两个问题。第一个是是否Thread.CurrentPrincipal传播到PLINQ中的线程。

答案是肯定的,因为这是 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使用,不是,也没有意义。

考虑到使用 aSynchronizationContext通常意味着序列化对特定上下文的调用(通常是线程,想想 UI 应用程序,但并非总是如此,考虑到ASP.NET 同步上下文),您会扼杀 PLINQ 从并行化中获得的任何收益,因为每个调用都必须通过SynchronizationContext.

PLINQ 的好处在于能够同时执行这些操作,而不是一次一个。

以下测试用例(与前一个非常相似)证明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 回答