FloatToStr / DateToStr的线程安全性

时间:2011-05-19 07:21:38

标签: multithreading delphi thread-safety delphi-xe

我刚刚在文档中发现FloatToStrDateToStr在他们的one-paramater重载中不是线程安全的。原因是他们访问存储在全局变量中的本地化信息。

我的问题是:如果我不在运行时更改格式设置,这是否具有实际意义?据我所知,只要每个人都只读取格式设置 - 即使是从多个线程中,我也是安全的。

这是真的还是我在这里遗漏了什么?

感谢。

4 个答案:

答案 0 :(得分:14)

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中看到了。

答案 1 :(得分:4)

当Application.UpdateFormatSettings(Delphi 7,不知道Delphi XE)为True时,即使全局设置也可以更改。当用户更改Windows的“区域和语言”选项时,这将反映在您的应用程序中。您可以通过将UpdateFormatSettings设置为False来规避这一点,但即便如此,您也无法确定,可能您使用的某些第三方库会对其进行更改。

我在自己的应用程序中遇到了一些问题:在我们的应用程序中,全局格式设置都没有改变,但仍然存在信息丢失,因为浮点数被转换为字符串,当字符串转换回浮点数时,格式设置是神奇的改变。 (所以你有这个:1.2 - >转换为字符串 - >'1.2' - >黑魔法改变了formatsettings.decimalseparator - >转换为float - > 12)。

我的建议:只使用非线程安全版本用于UI目的,这样用户可以按照他喜欢的方式查看日期和浮动,而对于其他一切,使用线程安全版本。然后,您的应用程序内部的转换将保持一致,并且不会给您带来意外。

答案 2 :(得分:3)

如果执行FloatToStrDateToStr时其他线程未更改全局设置,则可以。

编辑:要记住一件事:

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';

答案 3 :(得分:0)

我刚才有小数点分隔符的问题。 Delphi的流媒体系统(readcomponent / writecomponent等)只是将其改为'。'完成所有工作后,它会变回原来的状态。

所以,当我将这个系统用于我自己的目的(序列化/反序列化相当复杂的结构)并决定在单独的线程中进行,或者甚至在几个单独的线程中时,它会把我射到腿上:'。 '混合了','某处。

不幸的是,我在其他一些库中看到DecimalSeparator只是在程序中被改变而有意在最后改回它(最小心的是把它放在' finally'子句中),所以如果你的一些当其中一个库在单独的线程中运行时,使用StrToFloat等的线程安全版本执行代码是必要的。