如何在使用TPL时管理线程本地存储(TLS)?

时间:2011-11-01 00:24:06

标签: .net asynchronous threadpool task-parallel-library thread-local-storage

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

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

谢谢, 埃里克

3 个答案:

答案 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生成的线程。

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

答案 2 :(得分:0)

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

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