Как работает атрибут ThreadStatic?

Как работает атрибут [ThreadStatic]? Я предположил, что компилятор испустит некоторый IL, чтобы наполнить/получить значение в TLS, но, глядя на разборку, он, похоже, не делает этого на этом уровне.

Как продолжение, что произойдет, если вы положите его на нестатический член? У разработчика была ошибка, и компилятор даже не предупредил.

Обновление

Второй вопрос ответил здесь: ThreadStatic изменен с помощью Static С#

Ответ 1

Семантика реализации потока static находится ниже уровня IL, в компиляторе .NET jit. Компиляторы, которые испускают IL, например VB.NET и С#, не должны знать ничего о Win32 TLS, чтобы испускать IL-код, который может читать и писать переменную с атрибутом ThreadStatic. Нет ничего особенного в переменной, насколько известно С#, - это просто место для чтения и записи. Тот факт, что у него есть атрибут на нем, не имеет никакого отношения к С#. С# только нужно знать, чтобы испускать инструкции чтения или записи IL для этого имени символа.

"Тяжелая подтяжка" выполняется базовой средой CLR, которая отвечает за то, что IL работает над конкретной аппаратной архитектурой.

Это также объясняет, почему размещение атрибута на неуместном (нестатическом) символе не вызывает реакции со стороны компилятора. Компилятор не знает, какую специальную семантику требуется атрибут. Инструменты анализа кода, такие как FX/Cop, должны знать об этом.

Еще один способ взглянуть на это: CIL определяет набор областей хранения: статическое (глобальное) хранилище, хранилище элементов и хранилище стека. TLS не входит в этот список, очень вероятно, потому что TLS не обязательно должен быть в этом списке. Если инструкции чтения и записи IL достаточны для доступа к TLS, когда символ помечен атрибутом TLS, почему IL должен иметь специальное представление или обработку для TLS? Это не нужно.

Ответ 2

Как атрибут [ThreadStatic] работать?

Вы можете думать, что поле, помеченное ThreadStatic, прикреплено к потоку, а его время жизни сопоставимо с временем жизни потока.

Итак, в псевдокоде ThreadStatic аналогично (по семантике) значение ключа, привязанного к потоку:

Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myvariable"] += 1;

но синтаксис немного проще:

class MyClass {
  [ThreadStatic]
  static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;

что произойдет, если вы положите его на нестатический член?

Я считаю, что он игнорируется:

    class A {
        [ThreadStatic]
        public int a;
    }
    [Test]
    public void Try() {
        var a1 = new A();
        var a2 = new A();
        a1.a = 5;
        a2.a = 10;
        a1.a.Should().Be.EqualTo(5);
        a2.a.Should().Be.EqualTo(10);
    }

Кроме того, стоит упомянуть, что ThreadStatic не требует какого-либо механизма синхронизации по сравнению с обычными статическими полями (поскольку состояние не используется совместно).

Ответ 3

[ThreadStatic] создает изолированные версии одной и той же переменной в каждом потоке.

Пример:

[ThreadStatic] public static int i; // Declaration of the variable i with ThreadStatic Attribute.

public static void Main()
{
    new Thread(() =>
    {
        for (int x = 0; x < 10; x++)
        {
            i++;
            Console.WriteLine("Thread A: {0}", i); // Uses one instance of the i variable.
        }
    }).Start();

    new Thread(() =>
   {
       for (int x = 0; x < 10; x++)
       {
           i++;
           Console.WriteLine("Thread B: {0}", i); // Uses another instance of the i variable.
       }
   }).Start();
}

Ответ 4

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

Поля TLS - это доступ через регистры сегментов gs/fs. Эти сегменты используются ядрами ОС для доступа к памяти, специфичной для потока. Компилятор .net не выдает никакого IL для заполнения/получения значения в TLS. Это делается ядром ОС.