Ist es besser, zu verwenden TThread des „Synchronisieren“ oder Fensternachrichten für IPC zwischen Haupt- und untergeordneten Thread benutzen?

StackOverflow https://stackoverflow.com/questions/1806339

  •  05-07-2019
  •  | 
  •  

Frage

Ich habe eine ziemlich einfache Multi-Threaded-VCL-GUI-Anwendung mit Delphi geschrieben 2007. Ich mache einige Verarbeitung in mehreren untergeordneten Threads (bis zu 16 gleichzeitigen), die eine Gittersteuerung auf meinem Haupt-Formular aktualisieren muß (nur Strings ein Posting Gitter). Keiner der Kinder Fäden je each-andere reden.

Mein erster Entwurf beteiligt Aufruf TThread des „Synchronisieren“ Die Rastersteuer Form innerhalb des aktuell laufenden Fadens zu aktualisieren. Ich verstehe jedoch, dass Synchronisieren Aufruf im Wesentlichen ausgeführt, als ob es der Haupt-Thread ist, wenn sie aufgerufen. Mit bis zu 16 Threads gleichzeitig ausgeführt wird (und die meisten der Verarbeitung des Kindes Thread nehmen von <1 Sekunde bis ~ 10 Sekunden) wären Fenstermeldungen ein besseres Design?

Ich habe es bekommen an dieser Stelle zu arbeiten, wo das Kind Thread Beiträge ein Fenster Nachricht (bestehend aus einem Datensatz von mehreren Strings) und der Haupt-Thread einen Hörer hat und einfach aktualisiert das Gitter, wenn eine Nachricht empfangen wird.

Alle Meinungen über die beste Methode für die IPC in dieser Situation? Fenstermeldungen oder 'Synchronisieren'?

Wenn ich Fenster-Nachrichten verwenden, nicht Umwickeln Sie schlagen vor, den Code, wo ich an das Netz in einem TCriticalSection Post (betreten und verlassen) blockieren? Oder werde ich nicht über die Thread-Sicherheit sorgen, da ich im Hauptthread an das Netz gerade schreibe (obwohl innerhalb des Fensters Nachricht Handler-Funktion)?

War es hilfreich?

Lösung

Edit:

Es sieht aus wie viele der Details der Implementierung, da Delphi geändert haben 4 und 5 (die Delphi-Versionen ich immer noch für die meisten meiner Arbeit bin mit) und Allen Bauer hat die folgende kommentiert:

  

Seit D6, TThread verwendet nicht mehr Sendmessage. Es verwendet eine Thread-sichere Arbeitsschlange, wo die „Arbeit“ für den Hauptthread bestimmt platziert ist. Eine Nachricht wird an den Haupt-Thread geschrieben, dass die Arbeit, um anzuzeigen, zur Verfügung und die Hintergrund-Thread-Blöcke auf einer Veranstaltung. Wenn die Hauptnachrichtenschleife etwa im Leerlauf zu gehen, nennt es „CheckSynchronize“, um zu sehen, ob Arbeiten warten. Wenn ja, es verarbeitet sie. Sobald eine Arbeitseinheit abgeschlossen ist, wird das Ereignis, auf das der Hintergrund-Thread blockiert wird Vollendung anzuzeigen. Eingeführt in D2006 Zeitraum wurde TThread.Queue Methode hinzugefügt, die nicht blockiert.

Danke für die Korrektur. So nehmen Sie die Details in der ursprünglichen Antwort mit einem Körnchen Salz.

Das betrifft aber nicht wirklich die Kernpunkte. Ich behaupte immer noch, dass die ganze Idee von Synchronize() tödlich fehlerhaft ist, und dies wird offensichtlich sein, der Augenblick, wo man mehrere Kerne einer modernen Maschine besetzt zu halten versucht. Sie nicht „synchronisieren“ Threads, lassen Sie sie arbeiten, bis sie fertig sind. Versuchen Sie, alle Abhängigkeiten zwischen ihnen zu minimieren. Vor allem, wenn die GUI-Aktualisierung gibt es absolut nicht Grund dafür zu warten, zu vervollständigen. Ob Synchronize() SendMessage() oder PostMessage() verwendet, die resultierende Straßensperre ist das gleiche.


Was Sie hier präsentieren, ist keine Alternative überhaupt, als Synchronize() SendMessage() intern verwendet. Es ist also eher eine Frage, die Waffe, die Sie verwenden möchten, sich mit in den Fuß zu schießen.

Synchronize() hat seit der Einführung von TThread in der Delphi 2 VCL bei uns, das ist wirklich eine Schande, da es eines der größeren Design Fehlentwicklungen vermeiden in der VCL ist.

Wie funktioniert es? Es verwendet einen SendMessage() Anruf zu einem Fenster, das in dem Haupt-Thread angelegt wurde, und legt die Nachrichtenparameter die Adresse eines Objekts parameter Methode übergeben aufgerufen werden. Da Windows-Nachrichten werden nur in dem Thread verarbeitet werden, die das Zielfenster erstellt und betreibt seine Meldungsschleife wird dies den Faden suspendieren, um die Nachricht in Zusammenhang mit dem VCL-Haupt-Thread behandeln, rufen Sie die Methode und den Faden wieder aufnimmt erst nach dem Verfahren hat die Ausführung beendet.

Also, was ist falsch mit ihm (und was ist ähnlich falsch mit der Verwendung von SendMessage() direkt)? Mehrere Dinge:

  • jeden Thread Erzwingen Code im Zusammenhang mit einer anderen Thread Kräften zwei Fadenkontextwechsel auszuführen, die CPU-Zyklen unnötig brennt.
  • Während der VCL-Thread verarbeitet die Nachricht, um die synchronisierte Methode aufzurufen es keine andere Nachricht verarbeiten kann.
  • Wenn mehr als ein Thread diese Methode verwendet werden sie alle Block und warten Synchronize() oder SendMessage() zurückzukehren. Dadurch entsteht ein Riesen-Engpass.
  • Es ist ein Deadlock warten geschehen. Wenn der Faden Synchronize() oder SendMessage() ruft, während ein Synchronisationsobjekt zu halten, und der Faden VCL während der Verarbeitung der Nachricht benötigt, um die gleiche Synchronisationsobjekt erwerben die Anwendung perren.
  • Das gleiche kann der API-Aufrufe gesagt werden, für den Thread-Handle warten -. WaitForSingleObject() oder WaitForMultipleObjects() ohne einige Mittel verwenden, um Nachrichten zu verarbeiten, wird ein Deadlock verursachen, wenn der Thread diese Weise zu „synchronisieren“ mit dem anderen Thread benötigt

Also, was stattdessen zu verwenden? Mehrere Optionen, ich werde einige beschreiben:

  • Verwenden PostMessage() anstelle von SendMessage() (oder PostThreadMessage(), wenn die beiden Gewindegänge sowohl nicht der VCL sind Thread). Es ist jedoch wichtig, nicht alle Daten in den Nachrichtenparametern zu verwenden, das nicht sein wird, mehr gültig, wenn die Nachricht eintrifft, als the Senden und Faden empfangen werden überhaupt nicht synchronisiert sind, so haben einige andere Mittel, um sicherzustellen, verwendet werden, dass eine beliebige Zeichenfolge, Objektverweis oder Teil des Speichers noch gültig sind, wenn die Nachricht verarbeitet wird, auch wenn das Senden Thread nicht einmal existieren mehr.

  • Thread-sichere Datenstrukturen erstellen, setzen Sie Daten, um sie von Ihrem Arbeitsthreads und verbrauchen sie aus dem Haupt-Thread. Verwenden Sie PostMessage() nur die VCL Thread zu alarmieren, dass neue Daten verarbeitet werden, ist da, aber nicht veröffentlichen Nachrichten jedes Mal. Wenn Sie einen kontinuierlichen Strom von Daten könnte man sogar die VCL-Thread Umfrage für Daten hat (vielleicht durch einen Timer), aber dies ist ein schlechter Version des Menschen nur.

  • Sie nicht die Low-Level-Werkzeuge überhaupt verwenden, nicht mehr. Wenn Sie zumindest auf Delphi 2007 sind, laden Sie die OmniThreadLibrary und im Hinblick auf die Aufgaben zu denken beginnen, nicht Threads . Diese Bibliothek hat viele Einrichtungen für den Datenaustausch zwischen Threads und Synchronisierung. Es hat auch eine Thread-Pool-Implementierung, die eine gute Sache ist - wie viel Threads Sie nicht nur von der Anwendung unabhängig verwendet werden sollen, sondern auch auf der Hardware läuft es auf, so viele Entscheidungen nur zur Laufzeit vorgenommen werden. OTL ermöglicht es Ihnen, Aufgaben auf einem Thread-Pool-Thread ausgeführt, so kann das System stimmt die Anzahl der gleichzeitigen Threads zur Laufzeit.

Edit:

Beim Wiederlesen merke ich, dass Sie nicht beabsichtigen, SendMessage() zu verwenden, aber PostMessage() - na ja, gelten einige der oben genannten dann nicht, aber ich werde es an Ort und Stelle belassen. Allerdings gibt es einige weitere Punkte in Ihrer Frage sind möchte ich ansprechen:

  

Mit bis zu 16 Threads gleichzeitig ausgeführt wird (und die meisten der Verarbeitung des Kindes Thread nehmen von <1 Sekunde bis ~ 10 Sekunden) würden Fenstermeldungen ein besseres Design sein?

Wenn Sie eine Nachricht von jedem Thread einmal pro Sekunde oder sogar längere Zeit zu veröffentlichen, dann ist das Design gut. Was sollten Sie nicht tun ist, schreiben Hunderte oder mehr Nachrichten pro Thread pro Sekunde, da die Windows-Nachrichtenwarteschlange eine endliche Länge und benutzerdefinierte Nachrichten hat, sollen nicht mit dem normalen Nachrichtenverarbeitung zu viel stören (Ihr Programm beginnen würde, nicht mehr reagiert erscheinen).

  

, wo das Kind Thread Beiträge eine Windows-Nachricht (bestehend aus einem Datensatz von mehreren Strings)

Ein Fenster Nachricht kann nicht einen Datensatz enthalten. Es trägt zwei Parameter, eine vom Typ WPARAM, die andere vom Typ LPARAM. Sie können nur einen Zeiger auf einen solchen Datensatz zu einem dieser Typen gegossen, so dass die Lebensdauer des Datensatzes muss irgendwie verwaltet werden. Wenn Sie dynamisch es zuweisen müssen Sie es auch befreien, was zu fehleranfällig ist. Wenn Sie einen Zeiger auf einen Datensatz auf dem Stapel oder zu einem Objektfeld übergeben müssen Sie sicherstellen, dass es nach wie vor gültig ist, wenn die Nachricht verarbeitet wird, die für geposteten Nachrichten als für gesendete Nachrichten schwieriger ist.

  

tun Sie den Code empfiehlt, Einwickeln, wo ich an das Netz schreiben in einem TCriticalSection (betreten und verlassen) blockieren? Oder werde ich nicht über die Thread-Sicherheit sorgen, da ich im Hauptthread an das Netz gerade schreibe (obwohl innerhalb des Fensters Nachricht Handler-Funktion)?

Es gibt keine Notwendigkeit, dies zu tun, da der PostMessage() Anruf sofort zurückkehren wird, so dass keine Synchronisation notwendig ist an diesem Punkt . Sie müssen auf jeden Fall über die Thread-Sicherheit sorgen, leider kann man nicht wissen, , wenn . Sie müssen sicherstellen, dass der Zugriff auf Daten Thread-sicher ist, von immer , um die Daten für den Zugriff sperrt, Synchronisationsobjekte verwenden. Es ist nicht wirklich eine Art und Weise zu erreichen, dass für Aufzeichnungen können die Daten immer direkt zugegriffen werden.

Andere Tipps

BTW, können Sie auch TThread.Queue() statt TThread.Synchronize() . Queue() ist die Asynchron-Version, ist es nicht blockiert den aufrufenden Thread:

(Queue ist seit D8 verfügbar).

Ich ziehe Synchronize() oder Queue(), weil es viel einfacher zu verstehen ist (für andere Programmierer) und eine bessere OO als einfache Nachricht zu senden (ohne Kontrolle darüber oder in der Lage, es zu debuggen!)

Während ich bin sicher, es gibt einen richtigen und einen falschen Weg. Ich habe geschrieben, beide Methoden unter Verwendung von Code und die, die ich immer wiederkommen, ist die Methode Sendmessage und ich bin mir nicht sicher, warum.

Die Verwendung des Sendmessage vs Synchronisieren wirklich keinen Unterschied machen. Beide arbeiten im Wesentlichen gleich. Ich denke, der Grund, warum ich immer wieder mit Sendmessage ist, dass ich eine größere Menge an Kontrolle wahrnehmen, aber ich weiß es nicht.

Die Sendmessage Routine bewirkt, dass der aufrufende Thread zu anhalten und warten, bis das Zielfenster der Nachricht gesendet beendet die Verarbeitung. Aus diesem Grunde die Hauptanwendung Faden im wesentlichen auf den anrufenden Kind-Thread für die Dauer des Anrufs syncronized. Sie brauchen nicht einen kritischen Abschnitt in den Windows-Message-Handler zu verwenden.

Die Datenübertragung ist im Wesentlichen eine Art und Weise, von dem anrufenden Thread zu dem Hauptanwendungsthread. Sie können einen Integer-Typ-Wert in dem Rück message.result aber nichts, das in dem Haupt-Thread auf ein Speicherobjekt verweist.

Da die beiden Fäden „synchronisierten“, dass dieser Punkt und die Haupt apps Thread derzeit auf dem Sendmessage reagiert gebunden wird dann auch, Sie brauchen sich keine Sorgen über andere Themen in den kommenden und Ihre Daten mit der gleichen Wegwerfen Zeit. Also nein Sie müssen Kritische Abschnitte oder andere Arten von Fadensicherheitsmaßnahmen keine Sorge über die Verwendung.

Für einfache Dinge können Sie eine einzelne Nachricht (wm_threadmsg1) definieren und die wparam und lparam Felder verwenden, übertragen (integer) Statusmeldungen hin und her. Für komplexere Beispiele können Sie eine Zeichenfolge, indem sie es über den lparam vorbei und typecasting es zurück zu einem longint passieren. A-la longint (pchar (myvar)) oder Verwendung PWideChar wenn Ihr mit D2009 oder neuer.

Wenn Sie bereits wurde es mit den Synchronize Methoden arbeiten, dann würde ich nicht darum kümmern, es Nacharbeit, eine Änderung vorzunehmen.

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