Для потока действительно ли безопасно СЧИТАТЬ переменную, установленную событием Delphi VCL?
Когда пользователь нажимает на VCL TCheckbox, основной поток устанавливает булевскую переменную на Проверенное состояние флажка.
CheckboxState := CheckBox1.Checked;
В любое время поток читает ту переменную
if CheckBoxState then ...
Не имеет значения, если поток "пропускает" изменение в булевской переменной, потому что поток проверяет переменную в цикл, поскольку это делает другие вещи. Таким образом, это будет видеть изменение состояния в конечном счете...
Действительно ли это безопасно? Или мне нужен специальный код? Действительно ли окружение является чтением, и запись переменной (в потоке и основном потоке соответственно) с критическим кодом называет необходимым и достаточным?
Как я сказал, не имеет значения, если поток получает "неправильное" значение, но я продолжаю думать, что могла бы быть проблема низкого уровня, если один поток пытается считать переменную, в то время как основной поток посреди записи его, или наоборот.
Мой вопрос подобен этому: Перекрестное чтение потока переменной, кто значение, не считают важным.
(Также связанный с моим предыдущим вопросом: Используя EnterCriticalSection в Потоке для обновления маркировки VCL)
Это безопасно по трем причинам:
Только один поток записывает в переменную.
Переменная состоит только из одного байта, поэтому невозможно прочитать несогласованное значение. Он будет прочитан либо как Истина
, либо как Ложь
. Не может быть проблем с выравниванием с булевыми значениями Delphi .
Компилятор Delphi не выполняет тщательных проверок, действительно ли переменная записана, и не «оптимизирует» любой код, если это не так. Нелокальные переменные будут считываться всегда, спецификатор volatile
не требуется.
При этом, если вы действительно не уверены в этом, вы можете использовать целочисленное
значение вместо логического и использовать функцию InterlockedExchange ()
для записи в переменную. Здесь это перебор, но это хорошая техника, о которой следует знать, потому что для значений размером в одно машинное слово он может устранить необходимость в блокировках.
Вы также можете заменить логическое значение
подходящим примитивом синхронизации, например событием, и установить на нем блок потока - это поможет вам устранить циклы занятости в потоке.
В вашем случае (свойство Проверено) операция чтения является атомарной, поэтому она безопасна. То же самое, что и со свойством TThread.Terminated; простые операции чтения и записи для правильно выровненных байтов, слов и двойных слов являются атомарными. Дополнительную информацию можно найти в документации Intel :
ГЛАВА 8 - УПРАВЛЕНИЕ МНОГОПРОЦЕССОРОМ
8.1.1 Гарантированные атомарные операции
Процессор Intel486 (и более новые процессоры с тех пор) гарантирует, что следующее базовые операции с памятью всегда будут выполняться атомарно:
Процессор Pentium (и более новые процессоры с тех пор) гарантирует, что следующие дополнительные операции с памятью всегда будут выполняться атомарно:
Для примера, который вы привели, это будет безопасно. Технически основная проблема возникает в случаях, когда размер вашей переменной превышает машинное слово, и таким образом вы можете получить не синхронизированные старшее слово и длинное слово. Однако для небольших значений это не является проблемой.
Если вы думаете, что это потенциальная проблема, например, при использовании указателя, то лучше использовать TCriticalSection для контроля чтения и записи элемента. Это достаточно быстро для всех практических ситуаций и гарантирует 100% безопасность.