Потокобезопасность FloatToStr / DateToStr - PullRequest
5 голосов
/ 19 мая 2011

Я только что нашел в документации, что FloatToStr и DateToStr не являются потокобезопасными при их перегрузках с одним параметром.Причина в том, что они получают доступ к информации о локализации, хранящейся в глобальных переменных.

Мой вопрос: имеет ли это практическое значение, если я не изменяю настройки формата во время выполнения?Насколько я понимаю, я в безопасности, пока все читают только настройки формата - даже из нескольких потоков.

Это правда или я что-то здесь упускаю?

Спасибо.

Ответы [ 4 ]

14 голосов
/ 19 мая 2011

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

DecimalSeparator := ',';
try
  s := FloatToStr(123.45);
finally
  DecimalSeparator := '.';
end;

Когда вам нужны настройки безопасности протектора и «локального» формата, вам необходимо использовать перегруженные функции, которые принимают в качестве последнего параметра: AFormatSettings: TFormatSettings.Таким образом, чтобы сделать вышеупомянутый поток кода безопасным, вы должны написать:

var
  fs: TFormatSettings;

GetLocaleFormatSettings(GetThreadLocale, fs);
fs.DecimalSeparator := ',';
s := FloatToStr(123.45, fs);

Примечания:

  1. GetLocaleFormatSettings и инициализация fs могут вызываться один раз, а затем fs может использоваться несколько раз.Это ускорит код.
  2. Вместо GetLocaleFormatSettings можно использовать TFormatSettings.Create.Я не уверен, когда это было введено, но я вижу это в Delphi XE.
4 голосов
/ 19 мая 2011

Даже глобальные настройки могут измениться, когда Application.UpdateFormatSettings (Delphi 7, не знаю о Delphi XE) имеет значение True. Когда пользователь изменяет региональные и языковые параметры Windows, это будет отражено в вашем приложении. Вы можете обойти это, установив UpdateFormatSettings в False, но даже в этом случае вы не можете быть уверены, возможно, есть какая-то сторонняя библиотека, которую вы используете, которая меняет ее.

У меня были некоторые проблемы с нашим собственным приложением: нигде в нашем приложении глобальные настройки формата не были изменены, но все же произошла потеря информации, потому что float был преобразован в строку, а когда строка была преобразована обратно в float, настройки формата были волшебным образом изменилось. (Итак, у вас было следующее: 1.2 -> конвертировать в строку -> '1.2' -> черная магия, которая изменила formatsettings.decimalseparator -> конвертировать в float -> 12).

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

3 голосов
/ 19 мая 2011

Если глобальные настройки не изменяются другим потоком во время выполнения FloatToStr или DateToStr, все в порядке.

РЕДАКТИРОВАТЬ: следует помнить одну вещь:

var
  // Note: Using the global FormatSettings variable corresponds to using the
  // individual global formatting variables and is not thread-safe.
  FormatSettings: TFormatSettings absolute CurrencyString;

Глобальная переменная выше - это просто псевдоним для глобальных переменных, перечисленных ниже.Их можно изменить либо через переменную FormatSettings, либо напрямую.

var
  // Important: Do not change the order of these declarations, they must
  // match the declaration order of the fields in TFormatSettings exactly!
  CurrencyString: string deprecated 'Use FormatSettings.CurrencyString';
  CurrencyFormat: Byte deprecated 'Use FormatSettings.CurrencyFormat';
  CurrencyDecimals: Byte deprecated 'Use FormatSettings.CurrencyDecimals';
  DateSeparator: Char deprecated 'Use FormatSettings.DateSeparator';
  TimeSeparator: Char deprecated 'Use FormatSettings.TimeSeparator';
  ListSeparator: Char deprecated 'Use FormatSettings.ListSeparator';
  ShortDateFormat: string deprecated 'Use FormatSettings.ShortDateFormat';
  LongDateFormat: string deprecated 'Use FormatSettings.LongDateFormat';
  TimeAMString: string deprecated 'Use FormatSettings.TimeAMString';
  TimePMString: string deprecated 'Use FormatSettings.TimePMString';
  ShortTimeFormat: string deprecated 'Use FormatSettings.ShortTimeFormat';
  LongTimeFormat: string deprecated 'Use FormatSettings.LongTimeFormat';
  ShortMonthNames: array[1..12] of string deprecated 'Use FormatSettings.ShortMonthNames';
  LongMonthNames: array[1..12] of string deprecated 'Use FormatSettings.LongMonthNames';
  ShortDayNames: array[1..7] of string deprecated 'Use FormatSettings.ShortDayNames';
  LongDayNames: array[1..7] of string deprecated 'Use FormatSettings.LongDayNames';
  ThousandSeparator: Char deprecated 'Use FormatSettings.ThousandSeparator';
  DecimalSeparator: Char deprecated 'Use FormatSettings.DecimalSeparator';
  TwoDigitYearCenturyWindow: Word deprecated 'Use FormatSettings.TwoDigitYearCenturyWindow';
  NegCurrFormat: Byte deprecated 'Use FormatSettings.NegCurrFormat';
0 голосов
/ 22 ноября 2015

У меня только что была проблема с десятичным разделителем.Система потоковой передачи Delphi (readcomponent / writecomponent и т. Д.) Просто меняет ее на '.'и после того, как вся работа сделана, она возвращается к тому, чем она была.

Итак, когда я использовал эту систему для своих собственных целей (сериализацию / десериализацию довольно сложной структуры) и решил сделать это в отдельном потоке, или даже в нескольких отдельных потоках, он бросил меня в ногу: '.'где-то были смешаны с ','.

К сожалению, я видел в некоторых других библиотеках, когда DecimalSeparator просто изменяется в процедуре с намерением изменить его обратно в конце (наиболее осторожные помещают его в предложение 'finally'), поэтому, если часть вашего кода выполняется, когда один из этих библиотек выполняется в отдельном потоке, использование потоково-безопасных версий StrToFloat и т. д. является обязательным.

...