Inhaltsverzeichnis

Yield

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);

Voraussetzungen

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 Oops2) 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 Oops4), und verhält sich der Yield wie ein Unregister, resp. führt der Yield zum Cleanup5).

Ablauf

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();
        }
    }
}

Yield unterhalb von GUI-Elementen

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:

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.

Ablaufdetails und Einflussnahme

Intern findet unterhalb der Yield-Methode einiges statt. Die genaue Folge der Aktionen ist folgendermaßen:

  1. Prepare - Vorbereitungen bei Einstieg in einer Yield-Methode
  2. Disable - falls ein GUI-Element angegeben wurde, wird diese vor Eingriffen geschützt
  3. Yield mit Timeout - Die Warteschleife bis Wiederaufnahme
  4. Durchstarten mit einer von folgenden Möglichkeiten
    • Resume - 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 wird
    • ErrorDetecting (Baustein ist noch registriert), ErrorDetected (Baustein ist nicht mehr registriert) - im Fehlerfall
  5. Done - 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 YieldStrategy7) 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.

1)
Run oder Call, kein Spawn
2)
Meldungstext: Controlflow was user-defined, no Yield possible
3)
Konkret: exe, vb50, vb60, execlr, aspclr, vsnet
4)
Die Meldung lautet: Yield not supported for this type of implementation
5)
Bei einem 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
6)
technische Steuerung oder Geschäftsvorfall
7)
Namespace TeamWiSE.Runtime.NativeSupport