Thread-Klasse in Delphi-(1)
Thread-Klasse in Delphi-(1) Raptor (Originalarbeit)
Schlüsselwort ThreadEventcriticalSectionSynchronize
Fadenklassen in Delphi
Raptors [Mentalstudio]
http://mental.mentu.com
(eins)
In Delphi gibt es eine Thread-Klasse-Thread, die zur Implementierung von Multi-Thread-Programmen verwendet wird. Synchronisierung sind fertig. Dies ist jedoch nicht die gesamte Programmierung von Multi-Threaded.
Ein Thread ist im Wesentlichen ein Code, der gleichzeitig in einem Prozess ausgeführt wird. Ein Prozess hat mindestens einen Thread, den sogenannten Hauptfaden. Es kann auch mehrere Kinderfäden geben. Wenn mehr als ein Thread in einem Prozess verwendet wird, wird er als "Multi-Threading" bezeichnet.
Wie ist das sogenannte "ein Stück Code" definiert? Es ist tatsächlich eine Funktion oder ein Prozess (für Delphi).
Wenn Sie Windows -API verwenden, um Threads zu erstellen, wird sie über eine API -Funktion namens CreateThead implementiert, die definiert ist als:
Handlekrattethead (
Lpecurity_attributeslpThreadattributes,
Dworddwstacksize,
Lpthread_start_routinelpStartAddress,
Lpvoidlpparameter,
DWORDDWCREATIONFLAGS,
LpdWordLpThreadid
);
Ihre Parameter sind wie in ihren Namen erwähnt, nämlich: Thread -Attribute (verwendet, um Thread -Sicherheitsattribute unter NT zu setzen, ungültig unter 9x), Stapelgröße, Startadresse, Parameter und Erstellungsflags (zum festgelegten Threads den Status zur Erstellungszeit festgelegt) , Thread ID, und geben Sie schließlich den Thread -Handle zurück. Die Startadresse ist der Eingang zur Fadenfunktion und der Faden endet, bis die Fadenfunktion endet.
Der Ausführungsprozess des gesamten Threads lautet wie folgt:
Da CreateThead -Parameter viele sind und es sich um eine Windows -API handelt, wird eine allgemeine Threading -Funktion in Cruntimelibrary bereitgestellt (theoretisch kann sie in jedem Betriebssystem verwendet werden, das Threads unterstützt):
unsignedLong_BeginThread (void (_userEntry*__ start) (void*), unsigned__stkSize, void*__ arg);
Delphi bietet auch eine ähnliche Funktion mit der gleichen Funktion:
FunctionBeginThread (SecurityAttributes: Zeiger; StackSize: LongWord; Threadfunc: tThreadfunc; Parameter: Zeiger; CreationFlags: Langwort; VarThreadid: Langword): Ganzzahl;
Die Funktionen dieser drei Funktionen sind im Grunde gleich. Der größte Unterschied zwischen Thread -Funktionen und allgemeinen Funktionen besteht darin, dass diese drei Thread -Funktionen, sobald die Thread -Funktion gestartet wird, zurückkehren und der Haupt -Thread weiterhin nach unten ausgeführt wird, während die Thread -Funktion in einem unabhängigen Thread ausgeführt wird Es dauert bis zur Ausführung und was bei der Rückkehr ist sich der Haupt -Thread nicht darum kümmert oder weiß es nicht.
Unter normalen Umständen endet nach der Rückgabe der Thread -Funktion der Thread. Aber es gibt andere Möglichkeiten:
Windows -API:
VoidexitThread (DWORDDWEXITCODE);
Cruntimelibrary:
void_endthread (void);
Delphiruntimelibrary:
ProcedureendThread (exitCode: Integer);
Um einige erforderliche Threaddaten (Status/Eigenschaften usw.) aufzuzeichnen, erstellt das Betriebssystem ein internes Objekt für den Thread. Der Faden endet.
Obwohl die Multi-Threading-Programmierung einfach mit API oder RTL (Runtimelibrary) durchgeführt werden kann, ist noch detailliertere Verarbeitung erforderlich.
Es ist auch sehr einfach, diese Klasse zu verwenden. Dies ist die Thread -Funktion, die im Thread ausgeführt wird. Für bestimmte Details hierzu werde ich sie hier nicht wiederholen. Weitere Informationen finden Sie in den entsprechenden Büchern.
In diesem Artikel werden wir diskutieren, wie die Thread-Klasse Threads zusammenfasst, dh, wir werden eingehende Forschungen zur Implementierung der Thread-Klasse durchführen. Weil es nur besser ist, es zu verwenden, wenn Sie es wirklich verstehen.
Nachfolgend finden Sie die Erklärung der Thread -Klasse in DelPhi7 (in diesem Artikel wird nur die Implementierung unter der Windows -Plattform erörtert, sodass der gesamte Code zum Linux -Plattform -Teil entfernt wird):
TThread = Klasse
Privat
Fhandle: Thandle;
Fthreadid: Thandle;
Freatesuspended: boolean;
Fterminiert: boolean;
Fsuspended: boolean;
Ffreeonterminate: boolean;
Ffinished: boolean;
FreturnValue: Ganzzahl;
Fonterminate: tnotifyEvent;
Fsynchronize: tsynchronizerecord;
Ffatalexception: tobject;
procedureCallonterate;
klassifizierensynchronisieren (Asyncrec: psynchronizerecord);
FunktionGetPriority: ThreadPriority;
Verfahrensetpriorität (Wert: TThreadPriority);
Verfahrensetsuspendiert (Wert: Boolean);
geschützt
procedurecheckThreadError (Errcode: Integer); Überlastung;
procedurecheckThreadError (Erfolg: boolean); Überlastung;
verarbeitetoterminiert; virtuell;
procedureExecute; virtuell; abstrakt;
Prozeduresynchronize (Methode: TThreadMethod); Überlastung;
PropertyReturnValue: IntegerreadFreturnValueWriteFreturnValue;
Eigentumsterminiert: booleanreadfterminiert;
öffentlich
ConstructorCreate (erstellt: boolean);
destructorderestroy; überschreiben;
Verfahrensabbau; überschreiben;
Verfahrensumme;
Verfahrensuspend;
Verfahren;
FunctionWaitfor: Langwort;
clascroceduresynchronize (tead: tThread; Amethod: tThreadMethod); Überlastung;
classProcedurestaticsynchronize (ITHREAD: TThread; Amethod: TThreadMethod);
PropertyFatalexception: tobjectradffatalexception;
PropertyFreeOnterminate: booleanreadffreeonterminateWriteffreeOnterate;
PropertyHandle: ThandleaDfhandle;
PropertyPriority: ThreadPriorityreadgetPriorityWritesetPriority;
PropertySuspended: booleanreadfsuspendedWritesetsSetsuspended;
PropertyThreadid: THANDLEADFTHREADID;
PropertyOrterate: tnotifyeVentreadFonterminateWriteFonterminate;
Ende;
Die Thread -Klasse ist eine relativ einfache Klasse in Delphi RTL, mit nicht vielen Klassenmitgliedern und die Klassenattribute sind sehr einfach und klar.
(fortgesetzt werden)
Thread-Klasse in Delphi-(2)
Thread-Klasse in Delphi-(2) Raptor (Originalarbeit)
Schlüsselwort ThreadEventcriticalSectionSynchronize
Fadenklassen in Delphi
Raptors [Mentalstudio]
http://mental.mentu.com
Zwei
Der erste ist der Konstruktor:
constructorthread.create (erstellt: boolean);
Beginnen
erbtecreate;
Addthread;
Fsuspended: = erzeugt;
FreateSuspended: = erstellt;
Fhandle: = beginThread (nil, 0,@threadproc, zeiger (self), create_suspended, fthreadid);
Iffhandle = 0Then
riseThead.createResfmt (@sthreadcreateError, [syserformessage (getlasterror)]);
Ende;
Obwohl dieser Konstruktor nicht viel Code hat, kann er als das wichtigste Mitglied angesehen werden, da der Thread hier erstellt wird.
Nach dem Aufrufen von Tobject.create durch erbte erbte, wird der erste Satz einen Prozess aufrufen: Addthread ist der Quellcode wie folgt:
procedureaddthread;
Beginnen
InterlockedIncrement (ThreadCount);
Ende;
Es gibt auch einen entsprechenden Removethead:
Verfahreneremovethead;
Beginnen
InterlockedDecrement (ThreadCount);
Ende;
Ihre Funktion ist sehr einfach, die Anzahl der Threads im Prozess durch Hinzufügen oder Verringern einer globalen Variablen zu zählen. Das hier verwendete häufig verwendete Inc/DEC -Verfahren ist jedoch nicht der häufig verwendete Inc/DEC -Prozess, sondern der InterlockedIncrement/InterlockedDecrement -Prozess wird verwendet. Aber es gibt einen größten Unterschied zwischen ihnen, dh InterlockedIncrement/InterlockedDecrement ist fadensicher. Das heißt, sie können sicherstellen, dass die Ausführungsergebnisse unter multithreading korrekt sind, aber Inc/dec kann nicht. Oder in Bezug auf die Theorie des Betriebssystems ist dies ein Paar "primitiver" Operationen.
Nehmen Sie das Hinzufügen als Beispiel als Beispiel, um den Unterschied in den Implementierungsdetails der beiden zu veranschaulichen:
Im Allgemeinen gibt es drei Schritte nach dem Zerlegen des Betriebs des Hinzufügens einer Speicherdaten:
1. Lesen Sie Daten aus dem Speicher
2. Fügen Sie eine Daten hinzu
3. Speichern Sie es im Speicher
Nehmen wir nun an, dass ein Betrieb von Hinzufügen eines mit Inc in einer Zwei-Thread-Anwendung auftreten kann:
1. Thread A liest Daten aus dem Speicher (vorausgesetzt, es ist 3)
2. Thread B liest Daten aus dem Speicher (auch 3)
3. Thread A fügt den Daten einen hinzu (jetzt ist es 4)
4. Thread B fügt den Daten einen hinzu (jetzt ist es auch 4)
5. Thread A speichert Daten in den Speicher (die Daten im Speicher sind jetzt 4)
6. Thread B speichert auch Daten in den Speicher (die Daten im Speicher sind jetzt noch 4, aber beide Threads fügen einen hinzu, was 5 sein sollte, sodass hier ein Fehlerergebnis angezeigt wird).
Es gibt kein Problem mit dem Interlockinkrementierungsprozess, da das sogenannte "Primitive" eine unterbrechungsfreie Operation ist, dh das Betriebssystem kann sicherstellen, dass vor Ausführung eines "Primitiven" keine Gewindeschaltung durchgeführt wird. Im obigen Beispiel kann Thread B erst nach dem Fertigstellen von Daten in den Speicher von Daten die Zahl von ihm abrufen und einen Vorgang hinzufügen, der sicherstellt, dass das Ergebnis selbst in Multi-Threading-Situationen definitiv korrekt ist.
Das vorherige Beispiel zeigt auch eine Situation von "Thread Access Conflict", weshalb Threads "Synchronize" erforderlich sind.
Apropos Synchronisation, es gibt eine Ablenkung: Li Ming, ein Professor an der Universität von Waterloo in Kanada, hat einst Einwände gegen die Übersetzung des Wortes synchronisiert, als "Synchronisation" in "Threadsynchronisation". Sehr vernünftig. In Chinesisch bedeutet "Synchronisation" gleichzeitig, während "Thread -Synchronisation" vermeiden soll, dass "gleichzeitig auftreten". Auf Englisch hat Synchronize zwei Bedeutungen: Eine ist die Synchronisation im traditionellen Sinne (tooccurattheSametime) und der andere "Koordination". Das Wort synchronisieren in der "Threadsynchronisation" sollte sich auf die letztere Bedeutung beziehen, dh "sicherstellen, dass mehrere Threads die Koordination beibehalten und Fehler vermeiden, wenn Sie auf dieselben Daten zugreifen." Es gibt jedoch immer noch viele Wörter wie diese, die in der IT -Branche nicht genau übersetzt werden. Es sollte geklärt werden.
Ich werde weit gehen und zum Konstruktor von Thread zurückkehren.
Fhandle: = beginThread (nil, 0,@threadproc, zeiger (self), create_suspended, fthreadid);
Hier verwenden wir die oben erwähnte Delphirtl -Funktion. Der dritte Parameter ist die oben erwähnte Thread -Funktion, dh der im Thread ausgeführte Codeteil. Der vierte Parameter ist der Parameter, der an die Thread -Funktion übergeben wurde. Hier ist das erstellte Thread -Objekt (d. H. Selbst). Unter anderem wird der fünfte verwendet, um den Thread nach der Erstellung zu suspendieren und nicht sofort auszuführen (die Arbeit des Starts des Threads wird in der Nachverarbeitung basierend auf der erstellten Flagge bestimmt), und der sechste ist, die Thread -ID zurückzugeben.
Schauen wir uns nun den Kern von TThread: The Thread Function ThreadProc an. Interessanterweise ist der Kern dieser Thread -Klasse nicht ein Mitglied des Threads, sondern eine globale Funktion (da die Parameterkonvention des Beginnthread -Prozesses nur globale Funktionen verwenden kann). Hier ist der Code:
FunctionThreadProc (Thread: tThread): Ganzzahl;
var
Freetread: Boolean;
Beginnen
versuchen
ifnotThread.terminedthen
versuchen
Thread.execute;
außer
Thread.ffatalException: = acquirexceptionObject;
Ende;
Endlich
Freetheread: = Faden.FreeOnterminate;
Ergebnis: = Thread.FreturnValue;
Thread.oterminat;
Thread.Finished: = True;
SignalSyncent;
IFFRETHREADHENTHREAD.FREE;
Endthread (Ergebnis);
Ende;
Ende;
Obwohl es nicht viel Code gibt, ist es der wichtigste Teil des gesamten Threads, da dieser Code Code ist, der tatsächlich in einem Thread ausgeführt wird. Das Folgende ist eine zeilenweise Erläuterung des Codes:
Beurteilen Sie zunächst die beendete Flagge der Thread -Klasse. führt im Wesentlichen den Ausführungscode in der abgeleiteten Klasse aus.
Ausführende Ausführung ist daher eine Thread -Funktion in einer Thread -Klasse.
Wenn eine Ausnahme in der Ausführung auftritt, wird das Ausnahmebobjekt durch accoirexceptionObject erhalten und im FFatalexception -Mitglied der Thread -Klasse gespeichert.
Schließlich werden einige Abschlussarbeiten erledigt, bevor der Faden endet. Die lokale Variable Freetread zeichnet die Einstellung der freeOntermined -Eigenschaft der Thread -Klasse auf und legt dann den Thread -Rückgabewert auf den Wert des Thread -Class -Rückgabewert -Attributs fest. Führen Sie dann die doterminierte Methode der Thread -Klasse aus.
Der Code der doterminierten Methode lautet wie folgt:
proceduretThread.Doterminat;
Beginnen
ifesigned (fonterminate) thenynchronize (callonterate);
Ende;
Es ist sehr einfach, die Callonterate -Methode durch Synchronize aufzurufen, und der Code der Callonterate -Methode lautet wie folgt, nämlich einfach das Onterterate -Ereignis:
proceduretThread.callonterminate;
Beginnen
ifesigned (fonterminate) thenfonterminate (self);
Ende;
Da das Onterterate -Ereignis synchronisiert wird, handelt es sich im Wesentlichen um kein Threadcode, sondern den Haupt -Thread -Code (siehe Analyse von Synchronize später).
Setzen Sie nach der Ausführung von Onterminate die fundierte Flagge der Thread -Klasse auf True.
Führen Sie als Nächstes den SignalSyncEvent -Prozess aus, und sein Code lautet wie folgt:
VerfahrenssignalSyncEvent;
Beginnen
SetEvent (syncEvent);
Ende;
Es ist auch sehr einfach, ein globales Ereignis einrichten: SynceEvent.