4 votes

Les threads de l'API Delphi Win CreateTimerQueueTimer et les plantages de FormatDateTime à l'abri des threads

C'est une question un peu longue, mais c'est parti. Il existe une version de FormatDateTime qui est censée être thread safe dans la mesure où l'on utilise

GetLocaleFormatSettings(3081, FormatSettings); 

pour obtenir une valeur et ensuite vous pouvez l'utiliser comme ceci ;

FormatDateTime('yyyy', 0, FormatSettings); 

Imaginez maintenant deux minuteries, l'une utilisant TTimer (intervalle disons 1000ms) et l'autre créée comme ceci (intervalle 10ms) ;

CreateTimerQueueTimer
(
  FQueueTimer, 
  0, 
  TimerCallback, 
  nil, 
  10, 
  10, 
  WT_EXECUTEINTIMERTHREAD
);

Maintenant, le point délicat, si dans le rappel et aussi l'événement de la minuterie vous avez le code suivant ;

for i := 1 to 10000 do
begin
  FormatDateTime('yyyy', 0, FormatSettings);
end;

Notez qu'il n'y a pas d'affectation. Cela produit des violations d'accès presque immédiatement, parfois 20 minutes plus tard, peu importe, à des endroits aléatoires. Maintenant, si vous écrivez ce code dans C++Builder, il ne se plante jamais. La conversion d'en-tête que nous utilisons est celle de JEDI JwaXXXX. Même si nous mettons des verrous dans la version Delphi autour du code, cela ne fait que retarder l'inévitable. Nous avons examiné les fichiers d'en-tête C d'origine et tout semble correct. Y a-t-il une façon différente pour le C++ d'utiliser le runtime Delphi ? La version thread safe de FormatDatTime semble être ré-entrante. N'importe quelle idée ou réflexion de quelqu'un qui aurait déjà vu cela auparavant.

UPDATE :

Pour affiner un peu le problème, FormatSettings est passé en tant que const, alors est-ce important s'ils utilisent la même copie (car il s'avère que passer une version locale dans l'appel de fonction donne le même problème) ? De plus, la version de FormatDateTime qui prend le FormatSettings n'appelle pas GetThreadLocale, parce qu'elle a déjà les informations Locale dans la structure FormatSettings (j'ai vérifié deux fois en parcourant le code).

J'ai mentionné l'absence d'affectation pour préciser qu'il n'y a pas d'accès au stockage partagé et qu'aucun verrouillage n'est donc nécessaire.

WT_EXECUTEINTIMERTHREAD est utilisé pour simplifier le problème. J'avais l'impression que vous ne deviez l'utiliser que pour des tâches très courtes car cela peut signifier qu'il manquera le prochain intervalle s'il exécute quelque chose de long ?

Si vous utilisez un TThread ordinaire, le problème ne se pose pas. Ce que je veux dire, je suppose, c'est que l'utilisation d'un TThread ou d'un TTimer fonctionne, mais pas celle d'un thread créé en dehors de la VCL. C'est pourquoi j'ai demandé s'il y avait une différence dans la façon dont C++ Builder utilise la VCL/Delphi RTL.

En outre, ce code, comme mentionné précédemment, échoue également (mais prend plus de temps), après un certain temps, CS := TCriticalSection.Create ;

  CS.Acquire;
  for i := 1 to LoopCount do
  begin
    FormatDateTime('yyyy', 0, FormatSettings);
  end;
  CS.Release;

Et maintenant, pour la partie que je ne comprends vraiment pas, j'ai écrit ceci comme suggéré ;

function ReturnAString: string;
begin
  Result := 'Test';
  UniqueString(Result);
end;

et ensuite à l'intérieur de chaque type de minuterie le code est ;

  for i := 1 to 10000 do
  begin
    ReturnAString;
  end;

Cela provoque les mêmes types de défaillances, comme je l'ai déjà dit, le défaut n'est jamais au même endroit dans la fenêtre du CPU, etc. Parfois, il s'agit d'une violation d'accès et parfois d'une opération de pointeur invalide. J'utilise Delphi 2009.

UPDATE 2 :

Roddy (ci-dessous) souligne que l'événement Ontimer (et malheureusement aussi Winsock, c'est-à-dire TClientSocket) utilise la pompe à message de Windows (en passant, il serait bien d'avoir des composants Winsock2 sympas utilisant IOCP et Overlapping IO), d'où la volonté de s'en éloigner. Cependant, quelqu'un sait-il comment voir quel type de stockage local de threads est configuré sur le CreateQueueTimerQueue ?

Merci d'avoir pris le temps de réfléchir et de répondre à ce problème.

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