5 votes

Thread-Safeness de FloatToStr / DateToStr

Je viens de trouver dans la documentation que FloatToStr y DateToStr ne sont pas thread-safe dans leurs surcharges à un paramètre. La raison en est qu'elles accèdent aux informations de localisation stockées dans les variables globales.

Ma question est la suivante : cela a-t-il un intérêt pratique si je ne modifie pas les paramètres du format au moment de l'exécution ? D'après ce que je comprends, je suis en sécurité tant que tout le monde ne lit que les paramètres de format - même à partir de plusieurs fils.

Est-ce vrai ou est-ce que je rate quelque chose ?

Merci.

14voto

robmil Points 312

FloatToStr, DateToStr et d'autres fonctions similaires lisent les paramètres de format global. Donc, si votre application ne modifie pas ces paramètres pour ces appels de fonction, alors elle est thread safe. Le code suivant à l'opposé n'est pas thread safe :

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

Lorsque vous avez besoin de la sécurité de la bande de roulement et des paramètres de format "locaux", vous devez utiliser des fonctions surchargées, qui prennent comme dernier paramètre : AFormatSettings : TFormatSettings. Ainsi, pour rendre le code ci-dessus thread safe, vous devez écrire :

var
  fs: TFormatSettings;

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

Notes :

  1. GetLocaleFormatSettings et l'initialisation de fs peuvent être appelés une fois, puis fs peut être utilisé plusieurs fois. Cela accélérera le code.
  2. Au lieu de GetLocaleFormatSettings, on peut utiliser TFormatSettings.Create. Je ne sais pas exactement quand cela a été introduit, mais je le vois dans Delphi XE.

4voto

The_Fox Points 5626

Même les paramètres globaux peuvent changer lorsque Application.UpdateFormatSettings (Delphi 7, je ne sais pas pour Delphi XE) est True. Lorsqu'un utilisateur modifie les options régionales et linguistiques de Windows, cela se reflète dans votre application. Vous pouvez contourner ce problème en définissant UpdateFormatSettings sur False, mais même dans ce cas, vous ne pouvez pas être sûr, il se peut qu'une bibliothèque tierce que vous utilisez le modifie.

J'ai eu quelques problèmes avec notre propre application : Les paramètres de formatage globaux n'étaient modifiés nulle part dans notre application, mais il y avait tout de même une perte d'informations car un flottant était converti en chaîne de caractères et lorsque la chaîne de caractères était reconvertie en flottant, les paramètres de formatage étaient magiquement modifiés. (Vous aviez donc ceci : 1.2 -> convertir en chaîne -> '1.2' -> magie noire qui a changé formatsettings.decimalseparator -> convertir en flottant -> 12).

Ma suggestion : n'utilisez la version non thread-safe que pour l'interface utilisateur afin que l'utilisateur puisse voir les dates et les flottants comme il l'entend, pour tout le reste, utilisez la version thread-safe. Les conversions dans votre application seront alors cohérentes et ne donneront pas de surprises.

3voto

Thorsten Engler Points 2103

Si les paramètres globaux ne sont pas modifiés par un autre thread alors que FloatToStr ou DateToStr sont exécutés, vous allez bien.

EDIT : une chose à garder à l'esprit :

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

La variable globale ci-dessus est juste un alias pour les variables globales listées ci-dessous. Il est possible de les modifier soit par la fonction FormatSettings variable ou directement.

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

0voto

Yuriy Afanasenkov Points 1310

J'ai juste eu un problème avec le séparateur décimal. Le système de streaming de Delphi (readcomponent/writecomponent etc.) le change simplement en '.' et après que tout le travail ait été fait, il revient à ce qu'il était.

Ainsi, lorsque j'ai utilisé ce système pour mes propres besoins (sérialisation/désérialisation d'une structure assez complexe) et que j'ai décidé de le faire dans un fil séparé, ou même dans plusieurs fils séparés, cela m'a mis la puce à l'oreille : les '.' étaient mélangés avec ',' quelque part.

Malheureusement, j'ai vu dans d'autres bibliothèques que DecimalSeparator est simplement changé dans la procédure avec l'intention de le changer à nouveau à la fin (les plus prudents le mettent dans la clause 'finally'), donc si une partie de votre code est exécutée quand une de ces bibliothèques tourne dans un thread séparé, l'utilisation de versions thread-safe de StrToFloat etc. est impérative.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X