Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
GBP
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
V3147. Non-atomic modification of volat…
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Диагностика микро-оптимизаций (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C#)
Дополнительная информация
Оглавление

V3147. Non-atomic modification of volatile variable.

11 Ноя 2019

Анализатор обнаружил неатомарное изменение 'volatile' переменной, которое может привести к состоянию гонки.

Известно, что использование модификатора 'volatile' гарантирует, что все потоки будут видеть актуальное значение соответствующей переменной. Модификатор 'volatile' используется для того, чтобы указать CLR, что все операции присвоения этой переменной и все операции чтения из неё должны быть атомарными.

Можно посчитать, что пометки переменных как 'volatile' будет достаточно, чтобы безопасно использовать все возможные операции присвоения в многопоточном приложении.

Помимо простых операций присвоения, существуют также операции, изменяющие значение переменной перед записью. К таким операциям можно отнести:

  • var++, --var, ...
  • var += smt, var *= smt, ...
  • ...

Такая запись выглядит как одна операция, но в действительности это целая последовательность операций чтения-изменения-записи.

Рассмотрим использование 'volatile' переменной в качестве счетчика (counter++).

class Counter
{
  private volatile int counter = 0;
  ....
  public void increment()
  {
    counter++; // counter = counter + 1
  }
  ....
}

В IL коде операция инкремента раскрывается в следующие команды:

IL_0001:  ldarg.0
IL_0002:  ldarg.0
IL_0003:  volatile.
IL_0005:  ldfld      int32
modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile)
VolatileTest.Test::val
IL_000a:  ldc.i4.1
IL_000b:  add
IL_000c:  volatile.
IL_000e:  stfld      int32
modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile)
VolatileTest.Test::val

Здесь и кроется состояние гонки. Предположим, что 2 потока одновременно работают с одним и тем же экземпляром объекта типа Counter и выполняют инкремент переменной 'counter', изначально проинициализированной значением 10. При этом, оба потока будут работать одновременно, выполняя промежуточные действия над переменной counter, каждый на своём собственном стеке (назовём эти промежуточные значения temp1 и temp2):

[counter == 10, temp1 == 10] Поток N1 считывает значение 'counter' на свой стек. (операция ldfld в IL)

[counter == 10, temp1 == 11] Поток N1 изменяет значение temp1 на своём стеке. (операция add в IL)

[counter == 10, temp2 == 10] Поток N2 считывает значение 'counter' на свой стек. (операция ldfld в IL)

[counter == 11, temp1 == 11] Поток N1 записывает temp1 в 'counter'. (операция stfld в IL)

[counter == 11, temp2 == 11] Поток N2 изменяет значение temp2 на своём стеке. (операция add в IL)

[counter == 11, temp2 == 11] Поток N2 записывает temp2 в 'counter'. (операция stfld в IL)

Ожидалось значение переменной 'counter' равное 12 (а не 11), так как 2 потока выполнили инкремент над одной и той же переменной. Также возможна ситуация, когда потоки выполнят инкремент друг за другом, и в таком случае все будет так, как и ожидалось.

Чтобы избежать подобного поведения неатомарных операций для разделяемых переменных, можно использовать:

  • Блок 'lock'
  • Методы атомарных операций класса Interlocked из библиотеки System.Threading
  • Функциональность блокировок класса Monitor из библиотеки System.Threading

Пример корректного кода:

class Counter
{
  private volatile int counter = 0;
  ....
  public void increment()
  {
    Interlocked.Increment(ref counter);  
  }
  ....
}

Данная диагностика классифицируется как:

Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять