难道PLINQ尊重的SynchronizationContext? [英] Does PLINQ respect SynchronizationContext?

查看:196
本文介绍了难道PLINQ尊重的SynchronizationContext?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我说,我有以下的code:

 的IPrincipal capturedPrincipal = Thread.CurrentPrincipal中;
。myseq.AsParallel()选择(X =>
{
    Thread.CurrenctPrincipal = capturedPrincipal;
    / *调用code与中科院*保护/
});
 

,以确保 发.CurrenctPrincipal 将传播到选择的代表将被执行的每一个线程。我意识到,如果我有合适的 SynchronizationContext的 成立,这将自动发生。因此,没有PLINQ使用的SynchronizationContext 排队上线程池工作项目是什么时候?如果没有,为什么?

P.S。

我觉得要注意的是,code以上是在IIS下承载WCF环境中执行重要的是/ WAS(不ASP.NET兼容)。

编辑: 找到<一href="http://stackoverflow.com/questions/8319439/does-the-thread-identity-get-transferred-when-using-plinq-extensions">a类似的问题确认相同的行为,我见证者。

EDIT2: 修改casperOne的测试了一下,它没有说主题ID的是相同的:

  [测试]
    公共无效的Test1()
    {
        VAR主要=新的GenericPrincipal(新GenericIdentity(测试),新的字符串[0]);
        = Thread.CurrentPrincipal中校长;
        INT主题ID = Thread.CurrentThread.ManagedThreadId;

        Enumerable.Range(0,4000).AsParallel()
            .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            。选择(X =&GT;
                {
                    Assert.AreSame(Thread.CurrentPrincipal中,本金);
                    Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId,主题ID);
                    返回X;
                })
            .ToArray();
    }
 

解决方案

有两个问题。第一个是与否 发.CurrentPrincipal 传播到线程 PLINQ

答案是肯定的,因为这是的 <部分code>执行上下文 和<一href="http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx"相对=nofollow>执行上下文从调用线程捕捉并复制到当一个新的线程/任务/线程池中的线程启动新/回收线程。

下面的测试案例(运行在.NET 4.0中)显示了这一点:

  [TestMethod的]
公共无效TestMethod1()
{
    //捕获当前登录的帐户。
    //可能是一个的GenericPrincipal以及一些随机值
    //在标识名称设置。
    IPrincipal的P =新的WindowsPrincipal(WindowsIdentity.GetCurrent());

    //设置当前主体。
    Thread.CurrentPrincipal中= P;

    //设置同步环境。
    SynchronizationContext.SetSynchronizationContext(
        新的SynchronizationContext());

    //上下文不为空。
    Assert.IsNotNull(SynchronizationContext.Current);

    // PLINQ。
    VAR plinqThreadDetails =
        //并进。这个数目必须是合理
        //高,迫使并行的PLINQ可能
        //使用这个线程,如果尺寸小。
        从i在Enumerable.Range(0,4000).AsParallel()。
            //强制并行化。在最好的,这是
            //建议。
            WithExecutionMode(ParallelExecutionMode.ForceParallelism)
        选择新{
            //这些值retreived另一个线程上。
            IdentityName = Thread.CurrentPrincipal.Identity.Name,
            Thread.CurrentThread.ManagedThreadId,
        };

    //是否有任何并行?
    布尔anyParallel = FALSE;

    //使断言。
    //托管线程ID是比当前的不同。
    的foreach(在plinqThreadDetails VAR plinqThreadDetail)
    {
        //但主要还是流动,即使在不同的
        // 线。
        Assert.AreEqual(Thread.CurrentPrincipal.Identity.Name,
            plinqThreadDetail.IdentityName);

        //更新任何并行。
        anyParallel |!=(plinqThreadDetail.ManagedThreadId =
            Thread.CurrentThread.ManagedThreadId);
    }

    //有*一些*并行化。
    Assert.IsTrue(anyParallel);
}
 

关于是否 的SynchronizationContext 用于PLINQ,它不是,它没有任何意义。

考虑到使用的SynchronizationContext 通常意味着序列的呼叫到一个特定的上下文(这通常是一个螺纹,认为用户界面的应用程序,但不总是,考虑到的 ASP.NET同步上下文),你会杀死任何收益的PLINQ将受益于并行化,因为每次调用将必须编组回通过的SynchronizationContext

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

下面的测试案例(大沿previous一个行)证明了的SynchronizationContext 未捕获PLINQ主题:

  [TestMethod的]
公共无效TestMethod2()
{
    //设置同步环境。
    SynchronizationContext.SetSynchronizationContext(
        新的SynchronizationContext());

    //上下文不为空。
    Assert.IsNotNull(SynchronizationContext.Current);

    // PLINQ。
    VAR plinqThreadDetails =
        //并进。这个数目必须是合理
        //高,迫使并行的PLINQ可能
        //使用这个线程,如果尺寸小。
        从i在Enumerable.Range(0,4000).AsParallel()。
            //强制并行化。
            WithExecutionMode(ParallelExecutionMode.ForceParallelism)
        选择新{
            //这些值retreived另一个线程上。
            SynchronizationContextIsNull =
                SynchronizationContext.Current == NULL,
            Thread.CurrentThread.ManagedThreadId,
        };

    //使断言。
    //是否有任何并行?
    布尔anyParallel = FALSE;

    //使断言。
    //在PLINQ线程上的同步上下文是
    //没有设置,只有在不同的线程。
    的foreach(在plinqThreadDetails VAR plinqThreadDetail)
    {
        //如果线程ID是不同的。
        如果(plinqThreadDetail.ManagedThreadId!=
            Thread.CurrentThread.ManagedThreadId)
        {
            //同步上下文为空。
            Assert.IsTrue(plinqThreadDetail.SynchronizationContextIsNull);

            //有东西在另一个线程。
            anyParallel = TRUE;
        }
        其他
        {
            //同步上下文不为空。
            Assert.IsFalse(plinqThreadDetail.SynchronizationContextIsNull);
        }
    }

    //有*一些*并行化。
    Assert.IsTrue(anyParallel);
}
 

Say, I have the following code:

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

to be sure Thread.CurrenctPrincipal will be propagated to every thread where Select's delegate will be executed on. I've realized that if I had proper SynchronizationContext set up this would happen automatically. So does PLINQ use SynchronizationContext when queuing work items on ThreadPool? And if no, why?

p.s.

I think it's important to note that the code above is executed in WCF environment hosted under IIS/WAS (no ASP.NET compatibility).

Edit: Found a similar question confirming same behavior I witness.

Edit2: Modified casperOne's test a bit and it fails saying threadid is the same:

    [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();
    }

解决方案

There are two questions here. The first is whether or not Thread.CurrentPrincipal is propagated to threads in PLINQ.

The answer is yes, as this is part of the ExecutionContext and the ExecutionContext is captured from the calling thread and copied to the new/recycled thread when a new thread/task/thread pool thread is started.

The following test case (run in .NET 4.0) shows this:

[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);
}

Regarding whether or not SynchronizationContext is used in PLINQ, it's not, and it doesn't make sense to.

Considering that using a SynchronizationContext usually means serializing a call to a particular context (which is usually a thread, think UI applications, but not always, given the ASP.NET synchronization context), you'd kill any gains that PLINQ would gain from parallelization because every call would have to be marshaled back through the SynchronizationContext.

The benefits in PLINQ come from being able to execute these operations at the same time, not one-at-a-time.

The following test case (very much along the lines of the previous one) proves that the SynchronizationContext is not captured for PLINQ threads:

[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);
}

这篇关于难道PLINQ尊重的SynchronizationContext?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆