ThreadStatic против ThreadLocal <T>: общий лучше, чем атрибут?

95

[ThreadStatic]определяется с помощью атрибута, а ThreadLocal<T>использует общий. Почему были выбраны разные дизайнерские решения? Каковы преимущества и недостатки использования универсальных атрибутов в этом случае?

user2341923
источник
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...
Стефан Штайнеггер,
4
@StefanSteinegger: Я был бы очень осторожным с использованием ThreadLocalили ThreadStaticс пула потоков. Эти значения будут оставаться в течение всего срока службы потока пула, а не только для задачи, которую вы ему назначаете. Это может доставить вам неприятности довольно неочевидными способами. См. Stackoverflow.com/questions/561518/… и аналогичные вопросы для получения дополнительной информации.
Джим Мишель
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
И полную статью можно найти здесь .
Даниэль Душек