Какие проблемы решает локальное хранилище потоков или какие преимущества предоставляет локальное хранилище потоков по сравнению со стандартной объектно-ориентированной идиомой создания отдельных экземпляров объектов, содержащих локальные данные потока?
Локальное хранилище потока позволяет предоставить каждому работающему потоку уникальный экземпляр класса, который очень полезен при попытке работать с классами, не поддерживающими потоки, или при попытке избежать требований синхронизации, которые могут возникнуть из-за общего состояния.
Что касается преимущества по сравнению с вашим примером - если вы порождаете один поток, то использование локального хранилища потоков мало по сравнению с передачей в экземпляре. ThreadLocal<T>
и подобные конструкции становятся невероятно полезными при работе (прямо или косвенно) с ThreadPool.
Например, у меня есть конкретный процесс, над которым я недавно работал, где мы выполняем очень сложные вычисления с использованием новой библиотеки параллельных задач в .NET. Определенные части выполненных вычислений могут быть кэшированы, и если кэш содержит конкретное совпадение, мы можем сэкономить немало времени при обработке одного элемента. Однако для кэшированной информации требовалось много памяти, поэтому мы не хотели кэшировать больше, чем последний шаг обработки.
Однако попытка разделить этот кеш между потоками проблематична. Для этого нам нужно было бы синхронизировать доступ к нему, а также добавить несколько дополнительных проверок внутри нашего класса, чтобы сделать их потокобезопасными.
Вместо этого я переписал алгоритм, чтобы каждый поток мог поддерживать свой собственный кеш в ThreadLocal<T>
. Это позволяет каждому из потоков поддерживать свой собственный кэш. Поскольку схема разбиения, используемая TPL, стремится объединять блоки элементов, локальный кэш каждого потока, как правило, содержит соответствующие требуемые значения.
Это устранило проблемы синхронизации, но также позволило нам сохранить наше кэширование на месте. В этой ситуации общая выгода была довольно большой.
Для более конкретного примера, посмотрите на этот пост в блоге, который я написал о агрегации с использованием TPL . Внутри класс Parallel использует ThreadLocal<TLocal>
всякий раз, когда вы используете перегрузку ForEach, которая поддерживает локальное состояние (и методы Parallel.For<TLocal>
тоже). Вот как локальное состояние поддерживается отдельно для каждого потока, чтобы избежать блокировки.