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

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

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

Обновить

Ответ на второй вопрос: ThreadStatic, модифицированный статическим C#

5 ответов

Решение

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

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

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

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

Как работает атрибут [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 не требует какого-либо механизма синхронизации по сравнению с обычными статическими полями (поскольку состояние не является общим).

[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();
}

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

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

Статическое поле, отмеченное значкомThreadStaticAttributeне распределяется между потоками. Каждый выполняющийся поток имеет отдельный экземпляр поля и независимо устанавливает и получает значения для этого поля. Если доступ к полю осуществляется в другом потоке, оно будет содержать другое значение.

Другие вопросы по тегам