我正在使用.AsParallel()。ForAll()在ASP.NET请求的上下文中并行枚举一个集合。枚举方法依赖于System.Threading.Thread.CurrentPrincipal。
我可以依赖于用于将System.Threading.Thread.CurrentPrincipal设置为处理ASP.NET请求的线程的HttpContext.Current.User的单个线程,还是我需要自己管理?< / p>
提出问题的另一种方法是PLINQ使用的线程是否继承了调用该操作的线程的标识?
答案 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()等。)