10

我想将日志记录上下文信息存储在 TLS 中,以便我可以在入口点设置一个值,并使该值在所有生成的堆栈中都可用。这很好用,但我也使用 TPL 和 ThreadPool。那么问题就变成了如何将 TLS 数据迁移到其他线程。我可以自己做这一切,但是我失去了像 Parallel.For 这样的好方法。

使用 TPL 时有什么方法可以复制 TLS 吗?当 C# 获得 await 功能时,这也将适用于它。

谢谢,埃里克

4

3 回答 3

5

通常,这是通过使用已经提供线程本地数据的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;
    });
于 2011-11-01T00:28:12.943 回答
4

我找到了另一个不需要代码的问题的解决方案。我能够使用 CallContext 将数据附加到“逻辑线程”。此数据从起始线程传输到由 TPL 以及 ThreadPool 生成的线程。

http://www.wintellect.com/CS/blogs/jeffreyr/archive/2010/09/27/logical-call-context-flowing-data-across-threads-appdomains-and-processes.aspx

于 2011-11-03T21:37:49.083 回答
0

当然,还有另一种选择:编写一个 TaskLocal(T) 类,就像我们所做的那样,它将存储基于当前任务,而不是当前线程。老实说,我不知道为什么微软没有将其作为其初始任务实施的一部分。

重要实现说明:因为调用 await 的 Task 代码可以拆分,并以不同的TaskId 恢复,所以您还需要做我们也做过的事情,并在 TaskLocal(T) 中实现一个方法,将新的 TaskIds 映射到以前的 TaskIds,然后保存任务开始时的原始TaskId,并在每次等待调用后映射它。

于 2015-07-21T17:34:08.720 回答