Entwickler-Ecke
Open Source Units - TFiber - Fiber Klasse für Delphi
delfiphan - Fr 04.09.09 19:27
Titel: TFiber - Fiber Klasse für Delphi
(Aus
diesem Post [
http://www.delphi-forum.de/viewtopic.php?t=94449] entstanden)
TFiber
Die Klasse TFiber stellt eine separate Ausführungsumgebung zur Verfügung, die unabhängig von Threads existiert. Diese hat einen eigenen Satz von Registers, Stack, Exception Chain und Instruction Pointer.
Im Gegensatz zu einem Thread wird ein Fiber nicht vom Betriebsystem verwaltet und ausgeführt, sondern muss manuell in einem Thread der Wahl ausgeführt werden. Daher sieht das Starten eines Fibers erst mal aus wie ein normaler Funktionsaufruf. Der Vorteil ist aber, dass die Ausführung eines Fibers an einer beliebigen Stelle gestoppt und zu einem späteren Zeit forgesetzt werden kann. Ausserdem ist es möglich, mit einem einfachen Funktionsaufruf an beliebiger Stelle im Kontrollfluss ein Fiber von einem Thread auf einen anderen zu transferieren.
Anwendungsfälle
- Fibers können verwendet werden, um an beliebiger Stelle während der Ausführung den Thread zu wechseln, bspw. um den MainThread freizugeben.
- Fibers können verwendet werden, um CoRoutinen zu implementieren.
- Fibers können die Anzahl Threads reduzieren, wenn sehr viele Ausführungspfade existieren, diese aber nicht ständig laufen. So kann der Fiber inaktiv sein, bis wieder Arbeit ansteht, um dann wieder in einem Thread aus einem Threadpool ausgeführt zu werden.
Features
- Transparentes Exception Handling über Thread-Grenzen hinweg.
- "Inline" Wechsel zwischen MainThread und Worker-Thread aus einem ThreadPool
- Direkter Transfer von einem Fiber in einen anderen möglich
Methoden von TFiber
-
SwitchToWorkerThread: Ausführung in einem WorkerThread fortführen
-
SwitchToMainThread: Ausführung im MainThread fortführen
-
Yield: Kontrolle an Thread zurückgeben
-
Transition: Kontrolle an anderen Fiber übergeben
-
Resume: Kontrolle von Thread an Fiber übergeben
Properties von TFiber
-
FreeOnTerminate: Instanz automatisch freigeben, wenn der Ausführungspfad des Fibers abgeschlossen ist.
Die Units mit Beispielprojekt im Anhang oder
hier [
http://www.tyberis.com/download/Fibers.zip]. Falls jemand nach einer ThreadPool Implementierung sucht ist hier eine mit dabei.
Beispiel
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure TForm1.Button1Click(Sender: TObject); begin
if TEventFiber.RerunInFiber(Self, Button1Click) then Exit;
try TFiber.Current.SwitchToWorkerThread;
finally TFiber.Current.SwitchToMainThread; end;
end; |
// Edit: Race condition fixed
delfiphan - Fr 02.04.10 15:42
Die Library scheint niemand zu mögen ;) habe sie jetzt noch weiter vereinfacht.
Bei Events vom Typ
TNotifyEvent muss man jetzt nur noch
TEventFiber.RerunInFiber ausführen, um die Vorteile von Fibers nutzen zu können. Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| procedure TForm1.Button1Click(Sender: TObject); begin
if TEventFiber.RerunInFiber(Self, Button1Click) then Exit;
try TFiber.Current.SwitchToWorkerThread;
finally TFiber.Current.SwitchToMainThread; end;
end; |
alzaimar - Fr 02.04.10 16:41
Das ist zu hoch für 99.9% aller Programmierer.
Martok - Fr 02.04.10 16:58
Nicht unbedingt zu hoch, aber sowas ohne echte Sprachfeatures zu machen, ist irgendwie... unschön.
Vielleicht könnte man da mit den DLangExtensions was machen?
Webo - Fr 02.04.10 18:01
Jetzt, wie ich mir den Eingangstext so durchgelesen habe scheint das ein sehr interessantes Thema zu sein. Auch wenn ich direkt nicht weiß, wo ich sowas einbauen könnte, ich werde gleich mal damit rumspielen. Mal schauen was sich so ergibt 8)
delfiphan - Fr 02.04.10 18:03
Ich habe diese Library verwendet, um bestehenden Code multi-threaded zu machen. Z.B. wechsle ich bei langen DB-Queries schnell in einen WorkerThread (Achtung, die DB-Library muss natürlich Thread-Safe sein!). Wenn die Query fertig ist, geht es wieder weiter mit dem bestehenden Kontrollfluss im MainThread. Interessant auch bei blockierenden Calls bei Indy-Komponenten.
Kompliziert ist das ganze nicht, zu mindest nicht in der Anwendung. Im Gegenteil, es soll Threading vereinfachen.
Wenn man mit TThread was machen will, muss man ja:
- Von TThread ableiten. Der Code, der im Thread läuft in eine Execute-Methode packen.
- Informationen, die der Thread braucht muss man separat übergeben z.B. über public Properties in der abgeleiteten Klasse.
- Um die Kontrolle zurück an den MainThread zu geben muss man Synchronize verwenden. Die nimmt wieder nur Methoden ohne Argumente entgegen. Man muss wieder mit irgend welchen privaten Variablen arbeiten, die vielleicht nur temporärer Natur sind.
- Bei Exceptions im Thread muss man die selbst wieder an den MainThread kommunizieren.
Martok hat folgendes geschrieben : |
sowas ohne echte Sprachfeatures zu machen, ist irgendwie... unschön. |
Da gebe ich dir Recht. Ich würde so eine Funktionalität auch nicht in die RTL/VCL aufnehmen, wenn ich bei Borland arbeiten würde. Aber praktisch finde ich die Library trotzdem.
Webo - Fr 02.04.10 18:31
delfiphan hat folgendes geschrieben : |
Kompliziert ist das ganze nicht, zu mindest nicht in der Anwendung. Im Gegenteil, es soll Threading vereinfachen. |
Das ist wohl war. Ich habe grade mal ein bisschen getestet und muss sagen: Werde ich jetzt öfters anwenden. Ist ja ein Kinderspiel damit, Sachen in Threads auszulagern ;-)
Webo - Fr 02.04.10 19:27
Luckie hat folgendes geschrieben : |
Vorsicht! Fibers sind keine Threads! |
Geht klar Meister :flehan: (:)) ... dann ist es eben "ein Kinderspiel, Sachen in Pseudo-Threads auszulagern". Nee, Spaß beiseite, danke der Richtigstellung, man will ja dazu lernen.
Ob Pseudo-Thread oder nicht, aufjedenfall eine sehr nette Sache.
delfiphan - Fr 02.04.10 19:49
Fibers sind keine Threads, das wurde hier nirgends behauptet. Wenn ich oben von Threads oder Threading spreche, meine ich auch Threads.
In dieser Library geht es darum, mittels Fibers Threading zu vereinfachen. Die Vereinfachung resultiert daraus, dass man jeweils selbst wählen kann, in welchem Thread ein bestimmter Fiber läuft und den Thread auch jederzeit wechseln kann.
Diese Library enthält TFiber, eine Klasse, die Fibers implementiert. Diese, zusammen mit anderen Klassen in der Library, vereinfachen Threading.
Delete - Fr 02.04.10 20:27
delfiphan hat folgendes geschrieben : |
Fibers sind keine Threads, das wurde hier nirgends behauptet. |
Dich meinte ich ja auch gar nicht, sondern Webo.
delfiphan - Sa 30.10.10 08:43
Hab die API noch ein wenig angepasst für Leute mit einer neueren Version von Delphi. Man kann einen Fiber nun als anonyme Methode von irgendwo aus starten.
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| procedure TForm1.Button1Click(Sender: TObject); begin
TFiber.Run( procedure var I: Integer; begin (Sender as TButton).Caption := 'Calculating...';
TFiber.Current.SwitchToWorkerThread; try for I := 0 to 99 do Sleep(100); finally TFiber.Current.SwitchToMainThread; end;
(Sender as TButton).Caption := 'Calculation completed'; end );
end; |
BenBE - Sa 30.10.10 23:47
Sollte das Switchen zwischen MainFiber und TaskFiber nicht von der Fiber-Klasse realisiert werden? Also zumindest für die Initialisierung und Finalisierung des Fibers?
bummi - So 31.10.10 10:11
Interessante Klasse, ich werde mal Versuchen das ganze zu verstehen.
delfiphan - So 31.10.10 12:43
BenBE hat folgendes geschrieben : |
Sollte das Switchen zwischen MainFiber und TaskFiber nicht von der Fiber-Klasse realisiert werden? Also zumindest für die Initialisierung und Finalisierung des Fibers? |
Ich versteh nicht ganz; es gibt (im Beispiel oben) nur einen Fiber. Der Fiber wird dann auf dem einen oder anderen Thread ausgeführt.
Ich hab jetzt die API für die 2010er Version vereinfacht, sodass es nur noch 1 Klasse gibt (TFiber).
Zum Ausführen in einem Fiber:
TFiber.Run(<anonyme Methode>)
Innerhalb eines Fibers kann über
TFiber.Current auf die aktuelle Instanz zugegriffen werden.
BenBE - So 31.10.10 16:52
Mich hatten die SwitchToMainThread und SwitchToWorkerThread-Aufrufe etwas irritiert. Daher die Frage.
delfiphan - So 31.10.10 17:10
SwitchToMainThread und SwitchToWorkerThread wird ja von der Fiber-Klasse realisiert. Die Methoden werden von der aktuellen Fiber-Instanz ausgeführt. Die aktuelle Instanz wird über TFiber.Current abgerufen. (Die ehem. TAnonymousFiber-Klasse leitete von TFiber ab, daher hat TFiber.Current die TAnonymousFiber-Instanz zurückgegeben. Die Klasse gibt's jetzt aber nicht mehr).
Sieht vielleicht auf den ersten Blick ungewöhnlich aus, aber schlussendlich spart es Tipparbeit. Mit der Klasse kann ich "freihändig" Fibers und Threads nutzen, d. h. ich muss weder eine Variable deklarieren noch eine Klasse implementieren. Um das Beispiel oben mit TThread zu realisieren, müsste man zuerst eine Klasse schreiben (von TThread ableiten), den Button als Feld zuweisen, die Methode Execute overriden und darin am Schluss wieder Synchronize aufrufen, um den Button-Text wieder anzupassen. Das ganze Exception-Handling im Thread kommt noch hinzu. Viel zu viel Aufwand für Implemenation, Testen und Wartung; ausserdem ist der Code nicht wirklich lesbarer. (bei dieser Argumentation müsste man sich aber fragen, ob Delphi die richtige Sprache dafür ist).
Fiji - So 27.07.14 13:32
It seems it will not work with 64 bit.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!