FloatToStr / DateToStr 的线程安全性

发布于 2024-11-08 01:49:26 字数 219 浏览 0 评论 0原文

我刚刚在文档中发现 FloatToStrDateToStr 在其单参数重载中不是线程安全的。原因是它们访问存储在全局变量中的本地化信息。

我的问题是:如果我不在运行时更改格式设置,这有任何实际意义吗?据我了解,只要每个人都只读取格式设置(即使是从多个线程),我就是安全的。

这是真的还是我在这里遗漏了一些东西?

谢谢。

I just found in the documentation that FloatToStr and DateToStr are not thread-safe in their one-paramater overloads. The reason is that they access localization information stored in global variables.

My question is: is this of any practical relevance if I do not change the format settings at runtime? As far as I understand it, I'm on the safe side as long as everyone only reads the format settings - even from multiple threads.

Is that true or am I missing something here?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一枫情书 2024-11-15 01:49:26

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. 可以使用 TFormatSettings.Create 代替 GetLocaleFormatSettings。我不确定它是什么时候引入的,但我在 Delphi XE 中看到了这一点。

FloatToStr, DateToStr and others similar functions are reading global format settings. So, if your application does not change these settings for these function calls, then it is thread safe. The following code on opposite is not thread safe:

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

When you need the tread safety and "local" format settings, then you have to use overloaded functions, which take as last parameter: AFormatSettings: TFormatSettings. So, to make above code thread safe you have to write:

var
  fs: TFormatSettings;

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

Notes:

  1. GetLocaleFormatSettings and fs initialization may be called once and then fs may be used multiple times. This will speedup the code.
  2. Instead of GetLocaleFormatSettings may be used TFormatSettings.Create. I am not sure when that was introduced, but I see that in Delphi XE.
伴梦长久 2024-11-15 01:49:26

当 Application.UpdateFormatSettings(Delphi 7,不知道 Delphi XE)为 True 时,甚至全局设置也可以更改。当用户更改 Windows 的区域和语言选项时,这将反映在您的应用程序中。您可以通过将 UpdateFormatSettings 设置为 False 来规避此问题,但即使这样您也不能确定,也许您使用的某些第三方库会更改它。

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

我的建议:仅将非线程安全版本用于 UI 目的,以便用户看到日期并按照他喜欢的方式浮动,对于其他所有内容,请使用线程安全版本。应用程序内的转换将保持一致,不会产生意外。

Even the global settings can change when Application.UpdateFormatSettings (Delphi 7, don't know about Delphi XE) is True. When a user changes the Regional and Language options of Windows, this will be reflected in your application. You can circumvent this by setting UpdateFormatSettings to False, but even then you can't be sure, maybe there is some third party library you use that changes it.

I had some problems with our own application: Nowhere in our application the global formatsettings were changed, but still there was information loss because a float was converted to a string and when the string was converted back to float, the formatsettings were magically changed. (So you had this: 1.2 -> convert to string -> '1.2' -> black magic that changed formatsettings.decimalseparator -> convert to float -> 12).

My suggestion: only use the not thread-safe version for UI purposes so the user sees dates and floats the way he likes them to see, for everything else, use the thread-safe version. Conversions inside your application will then be consistent and don't give surprises.

姐不稀罕 2024-11-15 01:49:26

如果在执行 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';

If the global settings are not changed by another thread while FloatToStr or DateToStr are executed you are fine.

EDIT: one thing to keep in mind:

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

The global variable above is just an alias for the global variables listed below. It is possible to change them either through the FormatSettings variable or directly.

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';
岁月无声 2024-11-15 01:49:26

我只是遇到了小数点分隔符的问题。 Delphi 的流系统(readcomponent/writecomponent 等)只需将其更改为“.”当所有工作完成后,它又变回原来的样子。

因此,当我将这个系统用于我自己的目的(序列化/反序列化相当复杂的结构)并决定在单独的线程中甚至在几个单独的线程中执行它时,它让我大吃一惊:“。”在某处与“,”混合。

不幸的是,我在其他一些库中看到,当 DecimalSeparator 在过程中简单地更改时,打算在最后将其更改回来(最仔细的库将其放在“finally”子句中),因此,如果您的某些代码在这些库之一被执行在单独的线程中运行,使用 StrToFloat 等线程安全版本是必要的。

I just had problem with decimal separator. Delphi's streaming system (readcomponent/writecomponent etc) simply changes it to '.' and after all the work is done, it is changed back to whatever it was.

So, when I used this system for my own purposes (serializing/deserializing rather complex structure) and decided to do it in separate thread, or even in several separate threads, it shot me to the leg: '.' were mixed with ',' somewhere.

Unfortunately, I saw in some other libraries when DecimalSeparator is simply changed in procedure with intention to change it back at the end (most careful ones put it in 'finally' clause), so if some of your code is executed when one of these libs are running in separate thread, using thread-safe versions of StrToFloat etc. is imperative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文