使用PLINQ Extensions时是否传输了线程标识?

时间:2011-11-29 23:53:51

标签: c# asp.net multithreading plinq

我正在使用.AsParallel()。ForAll()在ASP.NET请求的上下文中并行枚举一个集合。枚举方法依赖于System.Threading.Thread.CurrentPrincipal。

我可以依赖于用于将System.Threading.Thread.CurrentPrincipal设置为处理ASP.NET请求的线程的HttpContext.Current.User的单个线程,还是我需要自己管理?< / p>

提出问题的另一种方法是PLINQ使用的线程是否继承了调用该操作的线程的标识?

3 个答案:

答案 0 :(得分:11)

不,标识不会自动传播到这些工作线程。实际上,如果您使用的组件是HttpContext.User,那么您可以捕获“main”线程中的当前“环境”HttpContext实例并将其传播到工作线程。这看起来像这样:

HttpContext currentHttpContext = HttpContext.Current;

myWorkItems.AsParallel().ForAll(wi =>
{ 
    HttpContext.Current = currentHttpContext;

    try
    {
        // anything called from here out will find/use the context of your original ASP.NET thread
    }
    finally
    {
       // Disassociate the context from the worker thread so that it is not held on to beyond its official lifetime
       HttpContext.Current = null;
    }
});

这是有效的,因为HttpContext.Current由线程静态支持,因此每个工作线程都将从主线程中分配实例,并且从该点开始的任何工作都会将其视为当前实例。

现在,你必须要知道HttpContext及其相关类的设计并不是线程安全的,所以这有点像黑客。如果你只是从房产中读取这不是一个真正的问题。如果您没有使用依赖HttpContext.Current的组件,那么不设置它会更“干净”,而只是直接在工作中使用捕获的currentHttpContext变量。

最后,如果您真正需要的是将当前主体传播到工作线程,那么您可以使用相同的方法来实现:

Principal logicalPrincipal = Thread.CurrentPrincipal;

myWorkItems.AsParallel().ForAll(wi =>
{ 
    Principal originalWorkerThreadPrincipal = Thread.CurrentPrincipal;
    Thread.CurrentPrincipal = logicalPrincipal;

    try
    {
        // anything called from here out will find the principal from your original thread
    }
    finally
    {
       // Revert to the original identity when work is complete
       Thread.CurrentPrincipal = originalWorkerThreadPrincipal;
    }
});

答案 1 :(得分:3)

这是CurrentPrincipal

背后的实施
public static IPrincipal CurrentPrincipal
{
    get
    {
        lock (CurrentThread)
        {
            IPrincipal threadPrincipal = CallContext.Principal;
            if (threadPrincipal == null)
            {
                threadPrincipal = GetDomain().GetThreadPrincipal();
                CallContext.Principal = threadPrincipal;
            }
            return threadPrincipal;
        }
    }
    set { CallContext.Principal = value; }
}

所有新创建的线程都将为null,它将从应用程序域中获取。所以应该没问题。不过你需要小心文化。它不会从启动线程派生。请参阅:Parallel Programing, PLINQ and Globalization

答案 2 :(得分:1)

通过 .AsParallel()边界传递 Principal 时要注意的一个微妙的事情:你的序列在哪里实现了?

这不是 .ForAll()的问题,但请考虑另一种情况:

var result = items.AsParallel().Select(MyTransform);

然后你将结果传递给其他地方,以便它穿过线程边界(例如,如果你将它从WCF动作方法中返回,可能就这样了。)

在这种情况下,当应用 MyTransform 时,Thread。 CurrentPrincipal 值可能包含意外的内容。

因此,此处的解决方法是在现场实现查询(通过调用 .ToArray() .ToList()等。)