[ThreadStatic]
визначається за допомогою атрибута, тоді як ThreadLocal<T>
використовує загальний. Чому були обрані різні дизайнерські рішення? Які переваги та недоліки використання загального над атрибутами в цьому випадку?
[ThreadStatic]
визначається за допомогою атрибута, тоді як ThreadLocal<T>
використовує загальний. Чому були обрані різні дизайнерські рішення? Які переваги та недоліки використання загального над атрибутами в цьому випадку?
Відповіді:
Щось у повідомленні блогу, зазначеному в коментарях, не є чітким, але я вважаю дуже важливим те, що [ThreadStatic]
не автоматично ініціалізує речі для кожного потоку. Наприклад, скажімо, що у вас є таке:
[ThreadStatic]
private static int Foo = 42;
Перший потік, який використовує це, побачить Foo
ініціалізований 42
. Але подальші потоки не будуть. Ініціалізатор працює лише для першого потоку. Отже, вам доведеться написати код, щоб перевірити, чи він ініціалізований.
ThreadLocal<T>
вирішує цю проблему, дозволяючи вам надати функцію ініціалізації (як показує блог Ріда), яка запускається до першого доступу до елемента.
На мій погляд, використання переваги [ThreadStatic]
замість ThreadLocal<T>
.
ThreadLocal<T>
доступне у .NET 4 та новіших версіях, а також ThreadStatic
атрибут також доступний у версії 3.5 та нижче.
ThreadLocal<T>
реалізує IDisposable
і, як правило, змушує вас також реалізовувати IDisposable
, що змушує ваших абонентів розпоряджатися вами, а отже, також реалізовувати IDisposable
...
ThreadLocal
або ThreadStatic
з потоками пулу. Ці значення залишатимуться протягом усього життя потоку пулу, а не лише для завдання, яке ви йому призначили. Це може доставити вам неприємності деякими досить неочевидними способами. Для отримання додаткової інформації див. Stackoverflow.com/questions/561518/… та подібні запитання.
static
? Див. Msdn.microsoft.com/en-us/library/…
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();
}
Основна ідея 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.