使用 PLINQ 扩展时是否会传输线程标识?

发布于 2024-12-19 02:32:57 字数 280 浏览 2 评论 0原文

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

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

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

I am using .AsParallel().ForAll() to enumerate a collection in parallel in the context of an ASP.NET request. The enumeration method relies on System.Threading.Thread.CurrentPrincipal.

Can I rely on the individual threads used to have their System.Threading.Thread.CurrentPrincipal set to the HttpContext.Current.User of the thread that is processing the ASP.NET Request or do I need to manage that myself?

Another way of asking the question is do the threads used by PLINQ inherit the identity of the thread that invoked the operation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

失与倦" 2024-12-26 02:32:57

不,身份不会自动传播到这些工作线程。事实上,如果您使用的组件是 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 及其相关类并未设计为线程安全的,因此这有点像黑客。如果您只读取属性,这并不是真正的问题。如果您没有使用依赖于 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;
    }
});

No, the identity will not be propagated to these worker threads automatically. If, in fact, the components you are using are HttpContext.User what you can do is capture the current, "ambient" HttpContext instance in your "main" thread and propagate it to your worker threads. That would look something like this:

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

This works because HttpContext.Current is backed by a thread static, so every worker thread will be assigned the instance from your main thread and any work done on it from that point will see that as the current instance.

Now, you have to be aware that HttpContext and its related classes were not designed to be thread safe, so this is a bit of a hack. If you're only reading from properties this isn't really a problem. If you are not using components that rely on HttpContext.Current then it would be "cleaner" to not set that and instead just use the captured currentHttpContext variable directly in the worker.

Finally, if all you really need is to propagate the current principal to the worker threads then you can do just that instead using the same approach:

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;
    }
});
请别遗忘我 2024-12-26 02:32:57

这是 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 和全球化

This is the implementation behind 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; }
}

All newly created threads will have null and it will be taken from application domain. So it should be ok. Nevertheless you need be careful with culture. It will not be derived from starting thread. See: Parallel Programing, PLINQ and Globalization

十年不长 2024-12-26 02:32:57

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

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

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

然后您将结果传递到其他地方,以便它跨越线程边界(这很可能是这样的) ,如果您从 WCF 操作方法返回它)。

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

因此,这里的解决方法是当场具体化查询(通过调用 .ToArray().ToList() 等)

One subtle thing to notice when passing Principal through .AsParallel() boundary: Where your sequence gets materialized?

This isn't a problem with .ForAll(), but consider another scenario:

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

Then you're passing result elsewhere so that it crosses thread boundary (which is likely, say, if you're returning it out of WCF action method).

In this case by the time MyTransform gets applied, Thread.CurrentPrincipal value might contain something unexpected.

So, the workaround here is to materialize query on the spot (by calling .ToArray(), .ToList(), etc.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文