首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PLINQ尊重SynchronizationContext吗?

PLINQ尊重SynchronizationContext吗?
EN

Stack Overflow用户
提问于 2012-10-24 11:51:21
回答 1查看 665关注 0票数 2

比如说,我有以下代码:

代码语言:javascript
复制
IPrincipal capturedPrincipal = Thread.CurrentPrincipal;
myseq.AsParallel().Select(x =>
{
    Thread.CurrenctPrincipal = capturedPrincipal;
    /*call code protected with CAS*/
});

可以肯定的是,Thread.CurrenctPrincipal将传播到执行Select委托的每一个线程。我已经意识到,如果设置了适当的SynchronizationContext,这种情况就会自动发生。那么,PLINQ在ThreadPool上排队工作项目时会使用SynchronizationContext吗?如果没有,为什么?

附注:

我认为重要的是要注意,上面的代码是在IIS/WAS下托管的WCF环境中执行的(没有ASP.NET兼容性)。

编辑:发现类似的问题证实了我看到的同样的行为。

Edit2:修改了casperOne的测试,但是失败了,说三次是一样的

代码语言:javascript
复制
    [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();
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-10-24 12:32:26

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

答案是肯定的,因为这是ExecutionContextExecutionContext从调用线程中捕获,并在启动新线程/任务/线程池线程时复制到新的/循环线程的一部分。

下面的测试用例(在.NET 4.0中运行)显示了以下内容:

代码语言:javascript
复制
[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);
}

关于SynchronizationContext是否在PLINQ中使用的问题,它没有,也没有任何意义。

考虑到使用SynchronizationContext通常意味着将调用序列化到特定的上下文(通常是线程,请考虑UI应用程序,但并不总是这样,给定ASP.NET同步上下文),您将扼杀PLINQ从并行化中获得的任何收益,因为每个调用都必须通过SynchronizationContext封送回封。

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

下面的测试用例(非常类似于前一个测试用例)证明了PLINQ线程没有捕获SynchronizationContext

代码语言:javascript
复制
[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);
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13048673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档