Frage

ich eine Multi-Threaded-Windows-Anwendung geschrieben, wo Gewinde:
   A - ist eine Windows-Form, die Interaktion mit dem Benutzer verarbeitet und zu verarbeiten, um die Daten von B.
   B - erzeugt gelegentlich Daten und übergibt es zwei A.

A threadsicher Warteschlange wird verwendet, um die Daten von Thread B zu A. Zum Bestehen der enqueue und dequeue Funktionen geschützt sind, um ein Windows-kritisches Abschnitt Objekte verwenden.

Wenn die Warteschlange leer ist, wenn die Enqueue-Funktion aufgerufen wird, wird die Funktion Postmessage verwendet A zu sagen, dass es Daten in der Warteschlange ist. Die Funktion überprüft der Anruf an Postmessage, um sicherzustellen, erfolgreich ausgeführt und wiederholt ruft Postmessage, wenn es nicht erfolgreich ist (Postmessage noch fehlschlagen hat).

Das funktionierte gut für einige Zeit, bis einer bestimmten Computer die gelegentliche Nachricht zu verlieren begann. Durch die lose ich meine, dass Postmessage erfolgreich in B gibt aber A erhält nie die Nachricht. Dies bewirkt, dass die Software eingefroren erscheinen.

Ich habe bereits mit ein paar annehmbaren Abhilfen kommen. Ich bin interessant zu wissen, warum Fenster werden diese Nachrichten verlieren und warum dies nur geschehen auf den einem Computer.

Hier sind die relevanten Teile des Codes.

// Only called by B
procedure TSharedQueue.Enqueue(AItem: TSQItem);
var
 B: boolean;
begin
  EnterCriticalSection(FQueueLock);
  if FCount > 0 then
    begin
      FLast.FNext := AItem;
      FLast := AItem;
    end
  else
    begin
      FFirst := AItem;
      FLast := AItem;
    end;

  if (FCount = 0) or (FCount mod 10 = 0) then // just in case a message is lost
    repeat
      B := PostMessage(FConsumer, SQ_HAS_DATA, 0, 0);
      if not B then 
  Sleep(1000); // this line of code has never been reached
    until B;

  Inc(FCount);
  LeaveCriticalSection(FQueueLock);
end;

// Only called by A 
function TSharedQueue.Dequeue: TSQItem;
begin
  EnterCriticalSection(FQueueLock);
  if FCount > 0 then
    begin
      Result := FFirst;
      FFirst := FFirst.FNext;
      Result.FNext := nil;
      Dec(FCount);
    end
  else
    Result := nil;
  LeaveCriticalSection(FQueueLock);
end;

// procedure called when SQ_HAS_DATA is received
procedure TfrmMonitor.SQHasData(var AMessage: TMessage);
var
  Item: TSQItem;
begin
  while FMessageQueue.Count > 0 do
    begin
      Item := FMessageQueue.Dequeue;
      // use the Item somehow
    end;
end;
War es hilfreich?

Lösung

Ist FCount auch durch FQueueLock geschützt? Wenn nicht, dann ist Ihr Problem liegt bei FCount erhöht wird, nachdem die gesendete Nachricht verarbeitet wird, schon.

Hier ist, was passiert sein könnte:

  1. B tritt in kritischen Abschnitt
  2. B ruft PostMessage
  3. A empfängt die Nachricht aber nicht alles tun, da FCount ist 0
  4. B-Schritte FCount
  5. B verlässt kritischen Abschnitt
  6. A sitzt da wie eine Ente

Eine schnelle Abhilfe wäre FCount zu erhöhen, bevor PostMessage aufrufen.

Beachten Sie, dass die Dinge schneller passieren können, als man erwarten würde (dh die Nachricht mit Postmessage geschrieben von einem anderen Thread gefangen und verarbeitet, bevor Sie eine Chance haben FCount ein paar Zeilen später zu erhöhen), vor allem, wenn Sie in einem sind echte Multi-threaded-Umgebung (mehr CPUs). Deshalb fragte ich früher, wenn die „Problem-Maschine“ hatte mehr CPUs / Cores.

Eine einfache Möglichkeit, Probleme wie diese zu beheben ist, den Code mit additonal Protokollierung Gerüst jedes Mal, geben Sie eine Methode, um sich einzuloggen, geben Sie / leave einen kritischen Abschnitt etc. Dann können Sie das Protokoll, um zu sehen, die wahre Reihenfolge der Ereignisse analysieren.

Auf einer separate Notiz, eine nette kleine Optimierung, die wie folgt in einem Producer / Consumer-Szenario durchgeführt werden kann, ist zwei Warteschlangen ein, anstatt zu verwenden. Wenn der Verbraucher die volle Warteschlange zu verarbeiten aufwacht, tauschen Sie die volle Warteschlange mit einer leeren und nur Verriegelungs- / Prozess die volle Warteschlange, während die neue leere Warteschlange kann, ohne dass die beiden Fäden bestückt werden versuchen, sich gegenseitig die Warteschlangen zu sperren. Sie würden immer noch, obwohl in der Vertauschung der beiden Warteschlangen eine Verriegelung benötigen.

Andere Tipps

  

Wenn die Warteschlange leer ist, wenn die Enqueue   Funktion aufgerufen wird, wird die Funktion   Verwenden von Postmessage A, dass es zu sagen,   Daten in der Warteschlange ist.

Sperren Sie die Nachrichtenwarteschlange, bevor Sie die Größe der Warteschlange und Ausgeben der PostMessage Überprüfung? Sie können eine Race-Bedingung erleben, wo Sie die Warteschlange überprüfen und nicht leer finden, wenn in der Tat eine der allerletzten Nachricht verarbeitet und bezieht sich auf Leerlauf gehen.

Um zu sehen, wenn Sie in der Tat erlebt eine Race-Bedingung und kein Problem mit PostMessage sind, die Sie mit Hilfe eines Ereignisse wechseln könnten. Der Arbeiter-Thread (A) würde auf das Ereignis warten stattdessen auf eine Nachricht zu warten. B würde einfach das Ereignis gesetzt anstatt eine Nachricht zu veröffentlichen.

  

Das funktionierte gut für einige Zeit   bis eines bestimmten Computer gestartet   verliert die gelegentliche Meldung.

Durch eine Chance, nicht die Anzahl der CPUs oder Kerne, die diese spezielle Computer anders haben als die anderen, wo sehen Sie kein Problem? Manchmal, wenn man von einer Single-CPU-Maschine in eine Maschine wechselt mit mehr als einer physischen CPU / Kern, neue Rennbedingungen oder Deadlocks auftreten können.

Könnte es sein, eine zweite Instanz unwissentlich ausgeführt wird und die Nachrichten zu essen, markiert sie als behandelt?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top