我想在TLS中存储日志记录上下文信息,以便我可以在入口点设置一个值,并在所有结果堆栈中使用该值。这项工作很好,但我也使用TPL和ThreadPool。然后问题就变成了如何将TLS数据迁移到其他线程。我自己可以做到这一切,但后来我失去了很好的方法,比如Parallel.For。
使用TPL时是否有某种方法可以复制TLS?当它获得await功能时,这也将适用于C#。
谢谢, 埃里克
答案 0 :(得分:5)
通常,这是通过使用已经提供线程本地数据的Parallel.For重载来处理的。
这个重载允许你提供一个初始化和一个终结委托,它有效地成为你的线程本地数据的每个线程的初始化,并在最后将一个简化函数“合并”在一起(每个线程运行一次) )。 I wrote about this in detail here
基本形式是做类似的事情:
object sync = new object();
double result = 0;
Parallel.For(0, collection.Count,
// Initialize thread local data:
() => new MyThreadSpecificData(),
// Process each item
(i, pls, currentThreadLocalData) =>
{
// Generate a NEW version of your local state data
MyThreadSpecificData newResults = ProcessItem(collection, i, currentThreadLocalData);
return newResults;
},
// Aggregate results
threadLocalData =>
{
// This requires synchronization, as it happens once per thread,
// but potentially simultaneously
lock(sync)
result += threadLocalData.Results;
});
答案 1 :(得分:4)
我找到了另一个不需要代码的问题的解决方案。我能够使用CallContext将数据附加到“逻辑线程”。此数据从起始线程传输到由TPL和ThreadPool生成的线程。
答案 2 :(得分:0)
当然还有另一种选择:像我们一样写一个TaskLocal(T)类,它将存储基于当前的Task,而不是当前的Thread。老实说,我不知道为什么微软没有将其作为初始任务实施的一部分。
重要实施说明:因为调用await的任务代码可以拆分,并作为不同的 TaskId继续,所以你还需要做我们也做的事情,并在TaskLocal(T)中实现一个方法将新的TaskId映射到以前的TaskId,然后在任务开始时保存原始的TaskId,并在每次等待调用后映射它。