Threadsicherer Zugriff auf Steuerelemente (Invoking)

  • Verwendet man in einer Form rechenintensive Vorgänge ohne diese in einem Thread auszulagern, dann reagiert die Form für die Zeit der Ausführung des Vorgangs nicht. Dies lässt den Nutzer glauben, das Programm hätte sich aufgehängt.
    (Typisches Beispiel: Der WindowsEditor mit großen Dateien)


    Es empfiehlt sich also diese Vorgänge in einen Thread auszulagern. Manchmal möchte man während des Vorgangs Informationen in der Form ausgeben. z.B. wenn man eine größere Datei verarbeitet eine ProgressBar aktualisieren.
    Versucht man von einem anderen Thread auf die Steuerelemente des WindowThreads zuzugreifen, wird dieser Zugriff standartmäßig verweigert.


    Code
    1. Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement progressBar1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.


    Diese Meldung lässt sich unterdrücken.

    C
    1. CheckForIllegalCrossThreadCalls = false;


    Dies ist aber in etwa so sinnvoll wie, wenn man öfter einen Autounfall hat und sich beschwert, dass man ständig den Airbag ins Gesicht bekommt. Und anstatt weniger Unfälle zu bauen, baut man lieber den Airbag aus.
    [Quelle: Stackoverflow]


    Die richtige Lösung ist Invoking. Man prüft vor dem Zugriff auf das Steuerelement, ob man einen thread-übergreifenden Vorgang durchführen würde (ein Invoke nötig ist).


    Dazu erstellt man eine Methode, welche die gewünschte Funkion ausführt (Man bezeichnet solche Methoden auch als getter und setter-Methoden).
    Die Funktion überprüft, ob ein Invoke nötig ist, und wenn ja fordert sie einen Invoke beim Steuerelement an und gibt sich selbst als Callback an. Das heist die Funktion ruft sich solange selbst auf, bis der Vorgang sicher durchführbar ist.
    Da wir hier mit einem Callback arbeiten, brauchen wir auch noch ein delegate.



    Jetzt kann man in seinem eigenen Thread SetProgressBarValue(10) aufrufen und die ProgressBar verändert sich ohne Probleme.



    Gruß
    florian0

  • Cool :)


    Man kann es aber auch noch etwas anders machen :)



  • Nein. So wie ich das interpretiere, geht es um das "Reservieren und Freigeben" in C# wäre das mit dem lock keyword durchzuführen. Es schließt einen gewissen Codeblock ein und Reserviert ihn. Als Beispiel könntest du eine Liste mit Threads halten, diese lässt du nun alle auf eine Queue los. Früher oder später wird das knallen (eher früher). Warum? Weil die Threads nicht gleichzeitig Adden können. Verwendet man nun das "Reservieren" wird der Codeblock mit dem ein Thread gerade beschäftigt ist, gesperrt und zwar solange, bis dieser an das ende des Codeblocks gelangt. Die anderen Threads warten derweil.


    Hier geht es aber darum GUI Operationen auszuführen. Hier muss nichts reserviert werden (Es sei denn man lässt 2 Threads auf die UI los, das ist aber mehr als dämlich). Es geht hier darum, dass der Thread das Control nicht "bearbeiten" darf, weil er es nicht erstellt hat. Er ist also nicht der "Besitzer" des Controls. Ein Invoke sagt dem GUI Thread nun, dass er die entsprechende Operation ausführen soll, weil der Work Thread das nicht darf.


    Und jetzt mal ganz allgemein: Ein Invoke ist auch nicht immer extrem ratsam. Das kostet. Und das ist bei größeren Anwendungen nicht unerheblich. Das kommt eher aus der C++ ecke. Normalerweise verwendet man dafür den Backgroundworker und aktualisiert GUI Felder im Progress Changed Ereignis.

    And the reign will kill us all
    We throw ourselves against the wall
    But no one else can see
    The preservation of the martyr in me


    Slipknot - Psychosocial