ThreadStatic проти ThreadLocal <T>: загальний краще, ніж атрибут?


95

[ThreadStatic]визначається за допомогою атрибута, тоді як ThreadLocal<T>використовує загальний. Чому були обрані різні дизайнерські рішення? Які переваги та недоліки використання загального над атрибутами в цьому випадку?


4
Див. Reedcopsey.com/2009/11/12/… - Хоча я не розумію, що це має відношення до роздумів ...
Джон Скіт,

Відповіді:


112

Щось у повідомленні блогу, зазначеному в коментарях, не є чітким, але я вважаю дуже важливим те, що [ThreadStatic]не автоматично ініціалізує речі для кожного потоку. Наприклад, скажімо, що у вас є таке:

[ThreadStatic]
private static int Foo = 42;

Перший потік, який використовує це, побачить Fooініціалізований 42. Але подальші потоки не будуть. Ініціалізатор працює лише для першого потоку. Отже, вам доведеться написати код, щоб перевірити, чи він ініціалізований.

ThreadLocal<T> вирішує цю проблему, дозволяючи вам надати функцію ініціалізації (як показує блог Ріда), яка запускається до першого доступу до елемента.

На мій погляд, використання переваги [ThreadStatic]замість ThreadLocal<T>.


20
За винятком можливо, що ThreadLocal<T>доступне у .NET 4 та новіших версіях, а також ThreadStaticатрибут також доступний у версії 3.5 та нижче.
Jeroen

2
І якщо ви не використовуєте ініціалізатори для встановлення значення, а замість цього встановлюєте його в якийсь пізніший момент після ініціалізації, використання [ThreadStatic] синтаксично чистіше.
Думка

9
І за винятком того, що ThreadLocal<T>реалізує IDisposableі, як правило, змушує вас також реалізовувати IDisposable, що змушує ваших абонентів розпоряджатися вами, а отже, також реалізовувати IDisposable...
Stefan Steinegger

4
@StefanSteinegger: Я був би дуже обережним, використовуючи ThreadLocalабо ThreadStaticз потоками пулу. Ці значення залишатимуться протягом усього життя потоку пулу, а не лише для завдання, яке ви йому призначили. Це може доставити вам неприємності деякими досить неочевидними способами. Для отримання додаткової інформації див. Stackoverflow.com/questions/561518/… та подібні запитання.
Jim Mischel

3
Чи не слід також оголосити поле у ​​прикладі static? Див. Msdn.microsoft.com/en-us/library/…
entheh

39

ThreadStatic Initialize лише для першого потоку, ThreadLocal Initialize для кожного потоку. Нижче наведена проста демонстрація:

    public static ThreadLocal<int> _threadlocal =
        new ThreadLocal<int>(() =>
        {
            return Thread.CurrentThread.ManagedThreadId;
        });

    public static void Main()
    {
        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("First Thread: {0}", x);
            }
        }).Start();

        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("Second Thread: {0}", x);
            }
        }).Start();

        Console.ReadKey();
    }

введіть тут опис зображення


15

Основна ідея ThreadStatic полягає у підтримці окремої копії змінної для кожного потоку .

class Program
    {
        [ThreadStatic]
        static int value = 10;

        static void Main(string[] args)
        {
            value = 25;

            Task t1 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T1: " + value);
            });
            Task t2 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T2: " + value);
            });
            Task t3 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T3: " + value);
            });

            Console.WriteLine("Main Thread : " + value);

            Task.WaitAll(t1, t2, t3);
            Console.ReadKey();
        }
    }

У наведеному вище фрагменті ми маємо окрему копію valueкожного потоку, включаючи основний.

введіть тут опис зображення

Отже, змінна ThreadStatic буде ініціалізована до значення за замовчуванням в інших потоках, крім потоку, в якому вона створена.

Якщо ми хочемо по-своєму ініціалізувати змінну в кожному потоці, використовуйте ThreadLocal.


1
І повну статтю можна знайти тут .
Даніель Душек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.