Как управлять локальным хранилищем потоков (TLS) при использовании TPL? - PullRequest
10 голосов
/ 01 ноября 2011

Я хочу сохранить информацию о контексте ведения журнала в TLS, чтобы я мог установить значение в точке входа и иметь это значение доступным во всех результирующих стеках.Это хорошо работает, но я также использую TPL и ThreadPool.Тогда возникает проблема, как перенести данные TLS в другие потоки.Я могу сделать все сам, но тогда я теряю хорошие методы, такие как Parallel.For.

Есть ли способ скопировать TLS при использовании TPL?Это также будет применяться к C #, когда он получит функцию ожидания.

Спасибо, Эрик

Ответы [ 3 ]

5 голосов
/ 01 ноября 2011

Как правило, это выполняется с помощью перегрузки 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;
    });
4 голосов
/ 04 ноября 2011

Я нашел другое решение проблемы, которое не требует кода.Мне удалось использовать 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

0 голосов
/ 21 июля 2015

Существует, конечно, еще одна альтернатива: напишите класс TaskLocal (T), как мы это сделали, который основывает хранилище на текущей Задаче, а не на текущем Потоке. Честно говоря, я понятия не имею, почему Microsoft не сделала этого в рамках своей первоначальной реализации Задачи.

Важное замечание по реализации: поскольку код задачи, который вызывает await, можно разделить и возобновить как другой TaskId, вам также нужно сделать то же, что и мы, и реализовать метод в TaskLocal (T), который сопоставляет новые TaskId с предыдущими, затем сохраняет исходный TaskId в начале задачи и отображает его после каждого ожидающего вызова.

...