Run
oder Call
, kein Spawn
Normalerweise ist die Ausführung eines Bausteins durch das Erreichen des Ende der Methode RunImplementation
(oder der jeweiligen Operation
-Methode) abgeschlossen. Die generierte Basisklasse trifft alle notwendigen Maßnahmen zur Anmeldung (Register
) und Abmeldung (Unregister
) des Bausteins bei der TAA-Infrastruktur. In manchen Fällen möchte man die Implementierung des Bausteins jedoch für spätere Wiederverwendung technisch aktiv erhalten, und dennoch die Kontrolle zum Aufrufer zurückgeben. Für solche Abläufe ist die Yield
-Anweisung verfügbar.
public void Yield(); public void Yield(ContainerControl Parent, YieldStrategy.DisableKind DisableKind = YieldStrategy.DisableKind.MinimalVisualImpact); public void Yield(YieldStrategy YieldStrategy);
Man beachte, dass zur Nutzung dieser Funktionalität, der jeweilige Baustein Yieldable
sein muss. Dafür ist zunächst wichtig, dass der Kontrollfluss zu dem Baustein von der TAA-Infrastruktur geregelt wird, und nicht, wie bei der Prepare
-Funktionalität, von der Anwendung versorgt wird. Auch muss der Baustein synchron1) gestartet worden sein, sonst wird der Yield
mit einem Oops
2) quittiert und abgelehnt. Implementierungstypen, bei dem der Baustein in einem eigenen Prozess3) läuft, sind grundsätzlich Yieldable
. Ein Baustein mit dem Implementierungstyp dllclr
kann als Yieldable
markiert werden. Das wird erreicht durch die Angabe des Flags
Y
in der ISPC
des Bausteins. Siehe dazu die Details zum Implementierungstyp DllCLR. Diese Markierung führt dann dazu, dass der Baustein beim Aufruf durch die TAA-Infrastruktur in einem eigenständigen Thread
gestartet wird. Wenn der Baustein nicht Yieldable
ist, und dennoch ein Yield
ausgeführt wird, führt das zur Laufzeit zu einem Oops
4), und verhält sich der Yield
wie ein Unregister
, resp. führt der Yield
zum Cleanup
5).
Wenn im Code eine Yield
-Anweisung ausgeführt wird, verhält sich der Code so, als würde an dieser Codestelle logisch gesehen zunächst eine Abmeldung (Unregister
) des aktiven Bausteins stattfinden. Dadurch erhält der Aufrufer dieses Bausteins die Kontrolle zurück, und wird seine Implementierung weiter durchlaufen. Der Baustein, der den Yield
vorgenommen hat, wartet nun an dieser Stelle. Sollte der Aufrufer im weiteren Verlauf den Baustein erneut aufrufen, so wird erkannt, dass dieser Baustein noch technisch vorhanden ist und auf eine Wiederaufnahme wartet. Die TAA-Infrastruktur benachrichtigt den wartenden Baustein, der sich an dieser Stelle nun logisch gesehen erneut anmeldet (Register
) und anschließend die Ausführung fortsetzen kann. Ein einfaches Beispiel:
protected override void OperationPruefen() { using (var db = NewDbConnection(DbConfigSettings)) { while (this.IsRegistered) { this.State.Active = CheckData(this, db); this.Yield(); } } }
Durch die Abfrage IsRegistered
wird die Schleife in dem Moment verlassen, wo nach dem Yield
offenbar keine neue Anmeldung stattgefunden hat. Das wäre bspw. dann der Fall, wenn der Aufrufer des Bausteins oder die umfassende Steuerung6) insgesamt beendet wird, und deswegen auch die noch wartenden Komponenten beendet werden sollen.
In dem obigen Beispiel ist zu beachten, dass nach einem Yield
nicht notwendigerweise die gleiche Operation ausgelöst wurde. Wenn das logisch gesehen passieren kann, sollte man derartiges prüfen oder eine dementsprechende andere Implementierung wählen. So könnte der obige Code auch wie folgt aussehen:
protected override void RunImplementation() { using (var db = NewDbConnection(DbConfigSettings)) { while (this.IsRegistered) { switch (this.Operation.Active) { case OperationEnum.Pruefen: this.State.Active = CheckData(this, db); break; case OperationEnum.Speichern: this.State.Active = SaveData(this, db); break; default: ErrorHandling(); break; } this.Yield(); } } }
Wenn die Yield
-Anweisung ausgeführt wird, sorgt die TAA-Infrastruktur dafür, dass eine ggf. vorhandene GUI-Oberfläche während der Warteschleife nicht blockiert wird. Man kann allerdings beim Aufruf der Yield
-Methode ein GUI-Element der Klasse ContainerControl
, also bspw. eine Form
oder ein Panel
, mitgeben. Dieses GUI-Element wird dann während der Yield
-Methode vor interaktiven Aktivitäten geschützt. Die Schutzmethode kann als weiteres Argument mitgegeben werden. Es stehen (derzeit) folgende Schutzarten zur Verfügung:
WindowsStandard
- das GUI-Element wird an geeigneter Stelle im Ablauf mit der Eigenschaft Enabled
aus- und wieder eingeschaltet.HideAndShow
- das GUI-Element wird an geeigneter Stelle im Ablauf mit der Eigenschaft Visible
aus- und eingeblendet.MinimalVisualImpact
- das GUI-Element wird mit internen Mechanismen geschützt. Im Gegensatz zu HideAndShow
bleibt das Element sichtbar. Im Gegensatz zu WindowsStandard
wird das Element nicht ausgegraut, sondern behält sein Aussehen, wodurch ein „Flackern“ verhindert wird. Außerdem kann das GUI-Element während des Wartens verschoben und in der Größe verändert werden.
Falls keine explizite Schutzmethode angegeben wurde, wird MinimalVisualImpact
verwendet.
internal void ReportIsOk() { this.State.Active = StateEnum.Ok; this.Yield(m_frm, YieldStrategy.DisableKind.HideAndShow); }
Ein weiteres Beispiel:
this.btnOK.Click += delegate { Module.State.SetOk(); Module.Yield(this); RefreshData(); }; this.btnNichtOK.Click += delegate { Module.State.SetNichtOk(); Module.Yield(this); RefreshData(); };
Als Besonderheit wird, falls das angegebene GUI-Element eine Form
ist, von der Yield
-Anweisung im Falle von Cleanup
oder Error
die Form
geschlossen, um damit automatisch die technische Ausführung des aktiven Threads
zu bewirken.
Intern findet unterhalb der Yield
-Methode einiges statt. Die genaue Folge der Aktionen ist folgendermaßen:
Prepare
- Vorbereitungen bei Einstieg in einer Yield
-MethodeDisable
- falls ein GUI-Element angegeben wurde, wird diese vor Eingriffen geschütztYield
mit Timeout
- Die Warteschleife bis WiederaufnahmeResume
- wenn der Baustein erneut aufgerufen wird. In diese Fall erfolgt einen erneuten Register
und wird ein ggf. gesperrtes GUI-Element wieder freigegeben. Wenn kein Resume
wird stattdessen ein Unregister
gemacht und das GUI-Element, insofern das eine Form
ist, geschlossen.CleaningUp
(Baustein ist noch registriert), CleanedUp
(Baustein ist nicht mehr registriert) - wenn der Aufrufer (optional - nur wenn CleanupWhenCallerDone
in der benutzten YieldStrategy
auf true
gesetzt wurde) oder die Gesamtsteuerung beendet wirdErrorDetecting
(Baustein ist noch registriert), ErrorDetected
(Baustein ist nicht mehr registriert) - im FehlerfallDone
- Abschluss als Gegenstück zum Prepare
Wenn eine Einflussnahme in diesem Ablauf benötigt wird, so kann das mit einem Objekt basierend auf die Klasse YieldStrategy
7) erreicht werden. Ein derartiges Objekt wird dann der Yield
-Methode mitgegeben und im oben beschriebenen Ablauf berücksichtigt:
internal void DataIsNotOk() { this.State.Active = StateEnum.NichtOk; this.Yield(Framework.StandardYieldStrategy); }
Oder:
internal void DataIsNotOk() { this.State.Active = StateEnum.NichtOk; this.Yield(new YieldStrategy { ControlToDisable = m_frm, Disable = YieldStrategy.DisableKind.HideAndShow, Timeout = 1000, HandlePrepare = module => SaveCurrentSettings(module), HandleDone = module => RestoreCurrentSettings(module), HandleTimeout = delegate { RefreshLog(); }, HandleErrorDetected = Errorhandler }); }
Die Reaktionen auf die einzelnen Ablaufbestandteile können dabei als delegate
, Lambda
-Ausdruck oder explizite Methode gesetzt werden.
Controlflow was user-defined, no Yield possible
exe
, vb50
, vb60
, execlr
, aspclr
, vsnet
Yield not supported for this type of implementation
Yield
in einem Baustein, der mittels Prepare
außerhalb von TAA-Einflüssen gestartet wurde, führt der Yield
allerdings nicht zum Cleanup
, sondern zum Error
TeamWiSE.Runtime.NativeSupport