4 Stimmen

Delphi Win API CreateTimerQueueTimer-Threads und thread-sichere FormatDateTime-Abstürze

Das ist eine etwas lange Frage, aber es geht los. Es gibt eine Version von FormatDateTime, die angeblich thread-sicher ist, da man

GetLocaleFormatSettings(3081, FormatSettings); 

um einen Wert zu erhalten, den Sie dann wie folgt verwenden können;

FormatDateTime('yyyy', 0, FormatSettings); 

Stellen Sie sich nun zwei Timer vor, einen mit TTimer (Intervall z.B. 1000ms) und einen weiteren Timer, der so erstellt wurde (Intervall 10ms);

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

Nun das Unangenehme, wenn Sie im Rückruf und auch im Timer-Ereignis den folgenden Code haben;

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

Beachten Sie, dass es keine Zuordnung gibt. Dies führt fast sofort zu Zugriffsverletzungen, manchmal 20 Minuten später, was auch immer, an beliebigen Stellen. Wenn Sie diesen Code nun in C++Builder schreiben, stürzt er nie ab. Die Header-Konvertierung, die wir verwenden, ist die von JEDI JwaXXXX. Selbst wenn wir in der Delphi-Version Sperren um den Code herum anbringen, verzögert das nur das Unvermeidliche. Wir haben uns die ursprünglichen C-Header-Dateien angesehen, und es sieht alles gut aus. Gibt es eine andere Art und Weise, wie C++ die Delphi-Laufzeit verwendet? Die thread-sichere Version von FormatDatTime ist anscheinend reentrant. Irgendwelche Ideen oder Gedanken von jemandem, der dies schon einmal gesehen hat.

UPDATE:

Um dies ein wenig einzugrenzen, wird FormatSettings als Konstante übergeben, so spielt es eine Rolle, wenn sie die gleiche Kopie verwenden (wie es sich herausstellt, Übergabe einer lokalen Version innerhalb des Funktionsaufrufs yeilds das gleiche Problem)? Auch die Version von FormatDateTime, die die FormatSettings nimmt nicht GetThreadLocale aufrufen, weil es bereits die Locale-Informationen in der FormatSettings-Struktur hat (ich doppelt überprüft, indem Sie durch den Code).

Ich habe keine Zuweisung erwähnt, um klarzustellen, dass auf keinen gemeinsamen Speicher zugegriffen wird, also kein Sperren erforderlich ist.

WT_EXECUTEINTIMERTHREAD wird zur Vereinfachung des Problems verwendet. Ich hatte den Eindruck, dass man es nur für sehr kurze Aufgaben verwenden sollte, weil es bedeuten kann, dass das nächste Intervall verpasst wird, wenn es etwas Langes ausführt?

Wenn Sie einen einfachen alten TThread verwenden, tritt das Problem nicht auf. Worauf ich hier hinaus will, ist wohl, dass die Verwendung eines TThread oder TTimer funktioniert, aber die Verwendung eines Threads, der außerhalb der VCL erstellt wurde, nicht. Deshalb habe ich gefragt, ob es einen Unterschied in der Art und Weise gibt, wie C++ Builder die VCL/Delphi RTL verwendet.

Nebenbei bemerkt scheitert dieser Code, wie bereits erwähnt, auch (dauert aber länger), nach einer Weile, CS := TCriticalSection.Create;

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

Und jetzt kommt der Teil, den ich wirklich nicht verstehe: Ich habe dies wie vorgeschlagen geschrieben;

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

und dann innerhalb jeder Art von Timer der Code ist;

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

Dies führt zu den gleichen Fehlern, da der Fehler, wie gesagt, nie an der gleichen Stelle innerhalb des CPU-Fensters usw. auftritt. Manchmal ist es eine Zugriffsverletzung und manchmal könnte es eine ungültige Zeigeroperation sein. Ich bin mit Delphi 2009 btw.

UPDATE 2:

Roddy (unten) weist darauf hin, dass das Ontimer-Ereignis (und leider auch Winsock, d.h. TClientSocket) die Windows-Nachrichtenpumpe verwenden (nebenbei bemerkt wäre es schön, einige nette Winsock2-Komponenten zu haben, die IOCP und Overlapping IO verwenden), daher der Versuch, davon wegzukommen. Jedoch weiß jemand, wie zu sehen, welche Art von Thread lokale Speicherung auf die CreateQueueTimerQueue eingerichtet ist?

Danke, dass Sie sich die Zeit genommen haben, über dieses Problem nachzudenken und es zu beantworten.

0voto

Roddy Punkte 64661

Zu Ihrer letzten Frage über Winsock und überlappende E/A: Sie sollten sich Folgendes genau ansehen Indy .

Indy verwendet blockierende E/A und ist eine gute Wahl, wenn Sie leistungsstarke Netzwerk-E/A benötigen, unabhängig davon, was der Hauptthread gerade tut. Jetzt, wo Sie das Multi-Threading-Problem geknackt haben, sollten Sie einfach einen weiteren Thread (oder mehrere) erstellen, um Indy für Ihre E/A zu verwenden.

0voto

Bruce Punkte 420

Das Problem mit Indy ist, dass es nicht sehr effizient ist, wenn man viele Verbindungen benötigt. Es erfordert einen Thread pro Verbindung (blockierende E/A), die überhaupt nicht skalierbar ist, daher der Vorteil von IOCP und Overlapping IO, es ist so ziemlich die einzige skalierbare Möglichkeit unter Windows.

0 Stimmen

Sicher, aber das ist in der Regel eine solche PITA, dass ich es in vielen Fällen als verfrühte Optimierung betrachten würde. Dieser Thread kann einige Hinweise für Sie haben, obwohl. stackoverflow.com/questions/37185

0voto

Serguzest Punkte 1277

Für update2 :

Es gibt eine freie IOCP-Sockelkomponente: http://www.torry.net/authorsmore.php?id=7131 (inklusive Quellcode)

"Von Naberegnyh Sergey N.. Hohe Socket-Server mit hoher Leistung basierend auf Windows Completion Port und unter Verwendung von Windows-Socket-Erweiterungen. IPv6 unterstützt. "

Ich habe es bei der Suche nach besseren Komponenten/Bibliotheken für die Neuarchitektur meines kleinen Instant-Messaging-Servers gefunden. Ich habe es noch nicht versucht, aber es sieht gut codiert als einen ersten Eindruck.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X