Список типов данных Delphi с «потокобезопасными» операциями чтения/записи?

StackOverflow https://stackoverflow.com/questions/510031

  •  21-08-2019
  •  | 
  •  

Вопрос

Являются ли логические переменные потокобезопасными для чтения и записи из любого потока?Я видел ссылки на некоторые группы новостей, подтверждающие это.Доступны ли другие типы данных?(Перечисляемые типы, возможно, короткие целые числа?)

Было бы неплохо иметь список всех типов данных, которые можно безопасно читать из любого потока, и еще один список, в который также можно безопасно записывать данные в любом потоке, не прибегая к различным методам синхронизации.

Это было полезно?

Решение

Обратите внимание, что практически все в Delphi можно сделать непотокобезопасным.В то время как другие упоминают проблемы выравнивания логических значений, это каким-то образом скрывает реальную проблему.

Да, вы можете прочитать логическое значение в любом потоке и записать логическое значение в любом потоке, если оно правильно выровнено.Но чтение из измененного логического значения не обязательно является «потокобезопасным».Предположим, у вас есть логическое значение, для которого вы установили значение true, когда вы обновили число, чтобы другой поток прочитал это число.

if NumberUpdated then
begin
  LocalNumber = TheNumber;
end;

Благодаря оптимизации процессора, TheNumber может быть прочитан до того, как будет прочитан NumberUpdated, таким образом, вы можете получить старое значение TheNumber, даже если вы обновили NumberUpdated последним.

Ака, ваш код может стать:

temp = TheNumber;
if NumberUpdated the
begin
  LocalNumber = temp;
end;

Имхо, основное правило:
«Чтение потокобезопасно.Запись не является потокобезопасной».
Итак, если вы собираетесь выполнить запись, защитите данные с помощью синхронизации. повсюду вы читаете значение, в то время как запись может потенциально происходить.
С другой стороны, если вы читаете и записываете значение только в одном потоке, это потокобезопасно.Таким образом, вы можете выполнить большую часть записи во временном расположении, а затем синхронизировать обновление данных всего приложения.

Бонусная аннотация:

VCL не является потокобезопасным.Сохраняйте все изменения пользовательского интерфейса в основном потоке.Также сохраняйте создание всех элементов пользовательского интерфейса в основном потоке.

Многие функции также не являются потокобезопасными, в то время как другие являются, это часто зависит от базовых вызовов winapi.

Я не думаю, что «список» будет полезен, поскольку «потокобезопасность» может означать много чего.

Другие советы

Вопрос не в том, являются ли типы данных потокобезопасными, а в том, что вы с ними делаете.Без блокировки ни одна операция не является потокобезопасной, включая загрузку значения, его изменение и обратную запись:увеличение или уменьшение числа, очистка или установка элемента в наборе — все они не являются потокобезопасными.

Существует ряд функций, которые позволяют выполнять атомарные операции:взаимосвязанное приращение, взаимосвязанное уменьшение и взаимосвязанный обмен.Это общая концепция, не специфичная для Windows, x86 или Delphi.Для Delphi вы можете использовать функции InterlockedFoo() Windows API, для них также существует несколько оболочек.Или напишите свой.Функции работают с целыми числами, поэтому с ними можно выполнять атомарное увеличение, уменьшение и обмен целыми числами (32 бита).

Вы также можете использовать ассемблер и префиксные операции с префиксом блокировки.

Для получения дополнительной информации см. также это StackOverflow вопрос.

В 32-битной архитектуре атомарными следует считать только правильно выровненные 32-битные или менее типы данных.32-битные значения должны быть выровнены по 4 (адрес данных должен делиться на четыре без остатка).Вероятно, вы не столкнетесь с чередованием на таком жестком уровне, но теоретически вы можете использовать двойную, Int64 или расширенную неатомарную запись.

Поскольку в состав современного процессора входит многоядерная RISC-обработка и отдельная кэш-память ядра, уже не существует «тривиальной» конструкции чтения или записи на языке высокого уровня (или, если уж на то пошло, многих когда-то «атомные» ассемблерные инструкции a-time 8086) можно считать атомарными.Действительно, если инструкция ассемблера специально не спроектирована как атомарная, она, вероятно, не является атомарной - и это включает в себя большинство механизмов чтения из памяти.Даже чтение длинного целого числа на уровне ассемблера может быть повреждено одновременной записью из другого ядра процессора, которое использует ту же память и использует асинхронные действия по обновлению кэша на уровне RISC-процессора.Помните, что в процессоре, состоящем из нескольких RISC-ядер, даже инструкции на языке ассемблера фактически представляют собой просто инструкции кода «более высокого уровня»!Никогда толком не знаешь, как они реализуются на битовом уровне, и это может быть не совсем то, что вы ожидали, если читали старое руководство по ассемблеру 8086 (одноядерный).Windows предоставляет атомарные операторы, совместимые с собственной системой, и вам рекомендуется использовать их, а не делать какие-либо базовые предположения об атомарных операциях.

Зачем использовать операторы Windows?Потому что одно из первых действий Windows — это определение того, на каком компьютере она работает.Один из ключевых аспектов, который он гарантирует правильность, — это какие атомарные операции существуют и как они будут работать.Если вы хотите, чтобы ваш код в будущем хорошо работал на любом будущем процессоре, вы можете либо продублировать (и постоянно обновлять) все эти усилия в своем собственном коде, либо воспользоваться тем фактом, что Windows сделала все это уже при запуске.Затем он включил необходимый код в свой API во время выполнения.

Прочтите страницы MSDN об атомарных операциях.Windows API предоставляет вам эту возможность.Иногда они могут показаться неуклюжими или неуклюжими, но они рассчитаны на будущее и всегда будут работать именно так, как написано на упаковке.

Откуда мне это знать?Ну, потому что если бы они этого не сделали, вы бы не смогли запустить Windows.Полная остановка.Не говоря уже о запуске собственного кода.

Всякий раз, когда вы пишете код, всегда полезно понимать бережливость и рассмотрим бритва Оккама.Другими словами, если Windows уже это делает, и для запуска вашего кода требуется Windows, тогда используйте то, что уже делает Windows, вместо того, чтобы пробовать множество альтернативных и все более сложных гипотетических решений, которые могут работать, а могут и не работать.Делать что-либо еще — это просто пустая трата вашего времени (если, конечно, вы не занимаетесь этим).

Код Indy содержит некоторые атомарные/потокобезопасные типы данных в IdThreadSafe.pas:

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TidthreadSafestringList и еще несколько ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top