我想将日志记录上下文信息存储在 TLS 中,以便我可以在入口点设置一个值,并使该值在所有生成的堆栈中都可用。这很好用,但我也使用 TPL 和 ThreadPool。那么问题就变成了如何将 TLS 数据迁移到其他线程。我可以自己做这一切,但是我失去了像 Parallel.For 这样的好方法。
使用 TPL 时有什么方法可以复制 TLS 吗?当 C# 获得 await 功能时,这也将适用于它。
谢谢,埃里克
我想将日志记录上下文信息存储在 TLS 中,以便我可以在入口点设置一个值,并使该值在所有生成的堆栈中都可用。这很好用,但我也使用 TPL 和 ThreadPool。那么问题就变成了如何将 TLS 数据迁移到其他线程。我可以自己做这一切,但是我失去了像 Parallel.For 这样的好方法。
使用 TPL 时有什么方法可以复制 TLS 吗?当 C# 获得 await 功能时,这也将适用于它。
谢谢,埃里克
通常,这是通过使用已经提供线程本地数据的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 以及 ThreadPool 生成的线程。
当然,还有另一种选择:编写一个 TaskLocal(T) 类,就像我们所做的那样,它将存储基于当前任务,而不是当前线程。老实说,我不知道为什么微软没有将其作为其初始任务实施的一部分。
重要实现说明:因为调用 await 的 Task 代码可以拆分,并以不同的TaskId 恢复,所以您还需要做我们也做过的事情,并在 TaskLocal(T) 中实现一个方法,将新的 TaskIds 映射到以前的 TaskIds,然后保存任务开始时的原始TaskId,并在每次等待调用后映射它。