Ich habe eine Anwendung, bei der die meisten Aktionen einige Zeit in Anspruch nehmen und ich möchte die Benutzeroberfläche jederzeit ansprechbar halten. Das Grundmuster einer vom Benutzer ausgelösten Aktion lautet wie folgt:
Ich habe mehrere Dinge versucht, um dies zu erreichen, aber alle verursachen auf lange Sicht Probleme (scheinbar zufällige Zugriffsverletzungen in bestimmten Situationen).
Synchronize
, um ein OnFinish
-Ereignis im Hauptthread aufzurufen. PostMessage
, um den GUI-Thread darüber zu informieren, dass die Ergebnisse fertig sind. Application.ProcessMessages
aufrufen), bis der Hintergrundthread beendet ist, und fahren Sie mit der Anzeige der Ergebnisse fort. Ich kann mir keine andere Alternative ausdenken und nichts davon funktionierte perfekt für mich. Was ist der bevorzugte Weg, dies zu tun?
1) Ist der 'Orignal Delphi' Weg, zwingt den Hintergrund Thread zu warten, bis die synchronisierte Methode ausgeführt wurde und das System zu mehr Deadlock-Potential ausgesetzt ist, als ich glücklich bin. TThread.Synchronize wurde mindestens zweimal neu geschrieben. Ich habe es einmal auf D3 benutzt und hatte Probleme. Ich schaute auf, wie es funktionierte. Ich habe es nie wieder benutzt.
2) I das Design, das ich am häufigsten verwende. Ich benutze App-Lifetime-Threads (oder Thread-Pools), erstelle Inter-Thread-Comms-Objekte und stelle sie in Hintergrundthreads unter Verwendung einer Producer-Consumer-Warteschlange basierend auf einem TObjectQueue-Nachkommen ein. Der Hintergrundthread / die Hintergrundthreads bearbeiten die Daten / Methoden des Objekts, speichern Ergebnisse im Objekt und, wenn sie vollständig sind, PostMessage () das Objekt (in lParam umgewandelt) zurück zum Hauptthread für die GUI-Anzeige der Ergebnisse in einer Nachricht handler, (lade das lParam erneut zurück). Die Hintergrund-Threads im Haupt-GUI-Thread müssen dann niemals auf demselben Objekt operieren und müssen nie direkt auf Felder von einander zugreifen.
Ich verwende ein verstecktes Fenster des GUI-Threads (erstellt mit RegisterWindowClass und CreateWindow), für die Hintergrundthreads zu PostMessage zu, comms-Objekt in LParam und 'target' TwinControl (normalerweise eine TForm-Klasse), als WParam. Das triviale wndproc für das ausgeblendete Fenster verwendet TwinControl.Perform () nur, um das LParam an einen Nachrichtenhandler des Formulars weiterzugeben. Dies ist sicherer als PostMessaging des Objekts direkt an ein TForm.handle - das Handle kann sich leider ändern, wenn das Fenster neu erstellt wird. Das ausgeblendete Fenster ruft nie RecreateWindow () auf und daher ändert sich sein Handle nie.
Producer-Consumer Queues 'out from GUI', inter-thread comms classes / objects und PostMessage () 'in GUI' WERDEN gut funktionieren - ich mache das schon seit Jahrzehnten.
Die Wiederverwendung der Kommunikationsobjekte ist auch ziemlich einfach - erstellen Sie einfach eine Ladung in einer Schleife beim Start (vorzugsweise in einem Initialisierungsabschnitt, so dass die Kommunikationsobjekte alle Formulare überleben), und schieben Sie sie in eine PC-Warteschlange - das ist Ihre Schwimmbad. Es ist einfacher, wenn die comms-Klasse ein privates Feld für die Pool-Instanz hat - die Methode 'releaseBackToPool' benötigt dann keine Parameter und stellt sicher, dass die Objekte immer wieder in ihren eigenen Pool freigegeben werden, wenn mehr als ein Pool vorhanden ist.
3) David Heffermans Kommentar kann nicht wirklich verbessert werden. Mach es einfach nicht.
Sie könnten Daten zwischen Threads als Nachrichten kommunizieren.
Thread1:
Faden 2:
Wenn Sie möchten, dass Ihre GUI nicht nur live ist, sondern auch auf einige Eingaben reagiert, erhalten Sie möglicherweise mehr als einen Thread ohne GUI, während die Eingabe verarbeitet wird, deren Verarbeitung lange dauert.
Tags und Links multithreading delphi delphi-xe asynchronous