我想在 TLS 中存储日志记录上下文信息,以便我可以在入口点设置一个值,并使该值在所有结果堆栈中可用。这工作得很好,但我也使用 TPL 和 ThreadPool。那么问题就变成了如何将 TLS 数据迁移到其他线程。我可以自己完成这一切,但是我会失去像 Parallel.For 这样的好方法。
使用 TPL 时是否有某种方法可以复制 TLS?当 C# 获得等待功能时,这也适用于它。
谢谢, 埃里克
通常,这是通过使用已提供线程本地数据的 Parallel.For 的重载来处理的。
此重载允许您提供初始化和终结委托,这实际上成为线程本地数据的每个线程的初始化,以及最后的缩减函数以将结果“合并”在一起(每个线程运行一次)。 我在这里详细写了这一点。
基本形式是执行以下操作:
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;
});
我找到了另一种不需要代码的问题解决方案。我能够使用 CallContext 将数据附加到“逻辑线程”。该数据从起始线程传输到 TPL 以及线程池生成的线程。
当然,还有另一种选择:像我们一样编写一个 TaskLocal(T) 类,该类将存储基于当前任务,而不是当前线程。老实说,我不知道为什么 Microsoft 没有将其作为其初始任务实现的一部分。
重要的实现注意事项:因为调用await的任务代码可以被拆分,并恢复为不同的TaskId,所以您还需要做我们也做过的事情,并在TaskLocal(T)中实现一个方法,将新的TaskId映射到以前的TaskId ,然后在任务开始时保存原始TaskId,并在每次await调用后映射它。