How to manage Thread Local Storage (TLS) when using TPL?

前端 未结 3 747
别那么骄傲
别那么骄傲 2021-02-14 10:48

I want to store logging context information in TLS so that I can set a value at the entry point, and have that value available in all resulting stacks. This work well, but I als

相关标签:
3条回答
  • 2021-02-14 11:09

    I found another solution to the problem that doesn't require code. I was able to use CallContext to attach data to a "logical thread". This data is transferred from the starting thread to threads generated by TPL as well as the ThreadPool.

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

    0 讨论(0)
  • 2021-02-14 11:17

    Typically, this is handled via using an overload of Parallel.For that already provides for thread local data.

    This overload allows you to provide an initialization and a finalization delegate, which effectively becomes an initialization per thread for your thread local data, and a reduction function at the end to "merge" the results together (which is run once per thread). I wrote about this in detail here.

    The basic form is to do something like:

    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;
        });
    
    0 讨论(0)
  • 2021-02-14 11:24

    There is, of course, yet another alternative: Write a TaskLocal(T) class, like we did, that bases the storage on the current Task, rather than the current Thread. Honestly, I have no idea why Microsoft didn't do this as part of their initial Task implementation.

    Important Implementation note: Because Task code that calls await can be split, and resume as a different TaskId, you also need to do what we also did, and implement a method in TaskLocal(T) that maps new TaskIds to previous ones, then save the original TaskId at the start of the Task, and map it after every await call.

    0 讨论(0)
提交回复
热议问题