Список типов данных Delphi с «потокобезопасными» операциями чтения / записи? - PullRequest
9 голосов
/ 04 февраля 2009

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

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

Ответы [ 5 ]

8 голосов
/ 04 февраля 2009

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

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

if NumberUpdated then
begin
  LocalNumber = TheNumber;
end;

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

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

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

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

Бонусная реклама:

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

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

Я не думаю, что "список" был бы полезен, поскольку "потокобезопасность" может означать много вещей.

7 голосов
/ 04 февраля 2009

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

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

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

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

5 голосов
/ 04 февраля 2009

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

2 голосов
/ 11 марта 2016

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

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

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

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

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

1 голос
/ 21 марта 2009

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

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TIdThreadSafeStringList и еще немного ...
...