当使用PLINQ Extensions时,线程标识是否被传输?

本文关键字:是否 标识 传输 线程 PLINQ Extensions | 更新日期: 2023-09-27 18:16:14

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

我可以依赖于使用它们的单独线程将它们的System.Threading.Thread.CurrentPrincipal设置为正在处理ASP的线程的HttpContext.Current.User吗?. NET请求还是我需要自己管理?

问这个问题的另一种方式是,PLINQ使用的线程是否继承了调用该操作的线程的身份?

当使用PLINQ Extensions时,线程标识是否被传输?

不,标识不会自动传播到这些工作线程。如果,事实上,你正在使用的组件是HttpContext.User,你可以做的是捕获当前的,"环境"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和它的相关类不是被设计为线程安全的,所以这是一个hack。如果您只从属性中读取,这不是问题。如果你不使用依赖于HttpContext.Current的组件,那么不设置它会"更干净",而是直接在worker中使用捕获的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;
    }
});

这是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,它将从应用程序域获取。所以应该没问题。然而,你需要注意文化。它不会从起始线程派生。参见:并行编程、PLINQ和全球化

当通过.AsParallel()边界传递Principal时要注意的一个微妙的事情:您的序列在哪里具体化?

这不是.ForAll()的问题,但是考虑另一个场景:

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

然后你在其他地方传递result,以便它跨越线程边界(这很可能,说,如果你从WCF动作方法返回它)。

在这种情况下,当MyTransform被应用时,线程。CurrentPrincipal value可能包含一些意想不到的内容。

因此,这里的解决方法是当场实现查询(通过调用. toarray (). tolist ()等)