我刚刚在文档中发现FloatToStr
和DateToStr
在他们的one-paramater重载中不是线程安全的。原因是他们访问存储在全局变量中的本地化信息。
我的问题是:如果我不在运行时更改格式设置,这是否具有实际意义?据我所知,只要每个人都只读取格式设置 - 即使是从多个线程中,我也是安全的。
这是真的还是我在这里遗漏了什么?
感谢。
答案 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 :(得分:4)
当Application.UpdateFormatSettings(Delphi 7,不知道Delphi XE)为True时,即使全局设置也可以更改。当用户更改Windows的“区域和语言”选项时,这将反映在您的应用程序中。您可以通过将UpdateFormatSettings设置为False来规避这一点,但即便如此,您也无法确定,可能您使用的某些第三方库会对其进行更改。
我在自己的应用程序中遇到了一些问题:在我们的应用程序中,全局格式设置都没有改变,但仍然存在信息丢失,因为浮点数被转换为字符串,当字符串转换回浮点数时,格式设置是神奇的改变。 (所以你有这个:1.2 - >转换为字符串 - >'1.2' - >黑魔法改变了formatsettings.decimalseparator - >转换为float - > 12)。
我的建议:只使用非线程安全版本用于UI目的,这样用户可以按照他喜欢的方式查看日期和浮动,而对于其他一切,使用线程安全版本。然后,您的应用程序内部的转换将保持一致,并且不会给您带来意外。
答案 2 :(得分:3)
如果执行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';
答案 3 :(得分:0)
我刚才有小数点分隔符的问题。 Delphi的流媒体系统(readcomponent / writecomponent等)只是将其改为'。'完成所有工作后,它会变回原来的状态。
所以,当我将这个系统用于我自己的目的(序列化/反序列化相当复杂的结构)并决定在单独的线程中进行,或者甚至在几个单独的线程中时,它会把我射到腿上:'。 '混合了','某处。
不幸的是,我在其他一些库中看到DecimalSeparator只是在程序中被改变而有意在最后改回它(最小心的是把它放在' finally'子句中),所以如果你的一些当其中一个库在单独的线程中运行时,使用StrToFloat等的线程安全版本执行代码是必要的。