none
Disposing Backgroundworker RRS feed

  • Frage

  • Hallo zusammen,

    meine Frage lautet ganz kurz: Wie dispose ich einen Backgroundworker RICHTIG?

    Folgendes halte ich für absoluten Käse, da der Worker sofort nach dessen start vom startenden Thread sofort disposed wird.

    void Foo() {
     using(BackgroundWorker worker = new BackgroundWorker) {
      ...
     }
    }
    

     

    Ein Disposing wäre evtl. noch in dem RunWorkerCompleted Event denkbar. Aber auch hier habe ich ehrlich gesagt noch Bauchschmerzen, da der Thread das Event gefeuert hat und somit quasi nocht "arbeitet".

    Gibt es dafür ein Muster oder ein best practices?


    Vielen Dank und viele Grüße
    Holger M. Rößler

    Freitag, 1. April 2011 19:34

Antworten

  • Montag, 4. April 2011 09:37
  • Hallo Holger,

    man muß einem MVP nicht einfach glauben. Ein Versuche (einige der) verbleibenden "?" zu eleminieren:

    Wie Hans Passant (nobugz) bereits erläutert, ist Dispose beim Backgroundworker ein Erbe von Component<sup>[2]</sup>.
    Dort mit NET 1.0 eingeführt, da man mit Komponenten damals Windows Forms im Blickfeld hatte, wo oft unmanaged gesprochen wird.
    Und die vornehmliche Aufgabe von IDisposable ist bekanntlich die Freigabe von unverwalteteten Ressourcen.

    Er ist primär von Component abgeleitet, um das Platzieren und Bearbeiten in einem Formular zu ermöglichen.
    Und macht sich einzig die Events zu nutze, um seine Ereignisse (DoWork, ProgressChanged, RunWorkerCompleted) abzulegen.
    Für die Benachrichtigungen (Fortschritt, Ende) verwendet er eine AsyncOperation<sup>[1]</sup>, die sich (ggf. via Finalizer) um ihre Entsorgung kümmert.

    Dispose kann er selbst nicht zuverlässig durchführen, da er keine Kenntnis darüber hat, welche Aufgaben
    er über den Thread durchführt, der mit RunWorkerAsync angestossen wird. Er gibt dabei via BeginInvoke
    die Kontrolle an den Thread (im ThreadPool) ab.

    Nur der Programmierer (von DoWork und Co) weiss, welche (unmanaged) Ressourcen die Methoden verwenden.
    Und der sollte sie entsprechend beim Abschluss (z. B. in RunWorkerCompleted) freigeben.

    Als lokale Variable wie in Deinem (fiktiven) Beispiel sollte man einen Backgroundworker i. a. nicht verwenden,
    da man damit keinerlei Zugriff mehr darauf hat, sobald der Bereich verlassen wurde. Und da die Operationen
    in einem separaten Thread laufen, ist deren Abschluss damit nicht sichergestellt (wahrscheinlich eher nicht).
    Und man verliert so die Möglichkeit, den Hintergrundthread via CancelAsync zur Aufgabe aufzufordern.

    Als "Best Practice" wäre ein Backgroundworker als Member Variable in einer Klasse, besser aufgehoben.
    Entweder in der Klasse, in der auch die Callback-Methoden hinterlegt sind (wie z. B. einem Formular)
    oder separat in einer Klasse, die eine vergleichbare Lebensdauer hat (z. B. einem Controller beim MVC).

    Wenn BackgroundWorker und die Callback-Methoden in der gleichen Instanz verwaltet werden, riskiert man nicht,
    dass er auf Dauer im Speicher verbleibt, weil noch Verweise über die Delegaten bestehen, siehe z. B.
    Events and Memory Leaks in .NET

    Gruß Elmar

    <sup>[1]</sup> Wenn Du bezüglich AsyncOperation an einem Exkurs interessiert bist, solltest Du Dir Mike Perez Artikelserie durchlesen:

    was hilfreich zum Verständnis des notwendigen Aufwandes ist. Und nicht nur beim Backgroundworker
    sondern auch bei Control.Invoke für die threadübergreifenden Zugriff zum Einsatz kommt.

    <sup>[2]</sup> Eine "richtige" Komponente ist er nicht, da er nicht den passenden Konstruktor (mit IContainer) implementiert,
    um in die components (eines Formulars) eingetragen zu werden - erkennbar daran, dass Site null ist.
    Und so gilt: Auch in einem Formular eingebettet, wird das Dispose nicht aufgerufen, da er in deren components
    nicht eingetragen wird. (nobugz Aussage bezüglich Dispose trifft auf ihn also nicht zu).
    Was als weiterer Beleg dienen kann, dass Dipose für einen Backgroundworker keinerlei Bedeutung hat.

     

    Dienstag, 5. April 2011 09:03
    Beantworter

Alle Antworten

  • Montag, 4. April 2011 09:37
  • Hi Stefan,

    omg und ich habe mir so einen Kopf gemacht. Das heisst echt, der BackgroundWorker muss gar nicht unbedingt disposed werden????  Ich hatte die von dir geposteten Links auch schon bereits gelesen, aber irgendwie fällt es mir schwer zu glauben, dass ein "disposable" Objekt nicht disposed werden muss. Aber wenns von einem MVP kommt, dann glaub ich es doch mal ;)


    Vielen Dank an dich!

    Viele Grüße
    Holger M. Rößler


    Montag, 4. April 2011 16:52
  • Hallo Holger,

    man muß einem MVP nicht einfach glauben. Ein Versuche (einige der) verbleibenden "?" zu eleminieren:

    Wie Hans Passant (nobugz) bereits erläutert, ist Dispose beim Backgroundworker ein Erbe von Component<sup>[2]</sup>.
    Dort mit NET 1.0 eingeführt, da man mit Komponenten damals Windows Forms im Blickfeld hatte, wo oft unmanaged gesprochen wird.
    Und die vornehmliche Aufgabe von IDisposable ist bekanntlich die Freigabe von unverwalteteten Ressourcen.

    Er ist primär von Component abgeleitet, um das Platzieren und Bearbeiten in einem Formular zu ermöglichen.
    Und macht sich einzig die Events zu nutze, um seine Ereignisse (DoWork, ProgressChanged, RunWorkerCompleted) abzulegen.
    Für die Benachrichtigungen (Fortschritt, Ende) verwendet er eine AsyncOperation<sup>[1]</sup>, die sich (ggf. via Finalizer) um ihre Entsorgung kümmert.

    Dispose kann er selbst nicht zuverlässig durchführen, da er keine Kenntnis darüber hat, welche Aufgaben
    er über den Thread durchführt, der mit RunWorkerAsync angestossen wird. Er gibt dabei via BeginInvoke
    die Kontrolle an den Thread (im ThreadPool) ab.

    Nur der Programmierer (von DoWork und Co) weiss, welche (unmanaged) Ressourcen die Methoden verwenden.
    Und der sollte sie entsprechend beim Abschluss (z. B. in RunWorkerCompleted) freigeben.

    Als lokale Variable wie in Deinem (fiktiven) Beispiel sollte man einen Backgroundworker i. a. nicht verwenden,
    da man damit keinerlei Zugriff mehr darauf hat, sobald der Bereich verlassen wurde. Und da die Operationen
    in einem separaten Thread laufen, ist deren Abschluss damit nicht sichergestellt (wahrscheinlich eher nicht).
    Und man verliert so die Möglichkeit, den Hintergrundthread via CancelAsync zur Aufgabe aufzufordern.

    Als "Best Practice" wäre ein Backgroundworker als Member Variable in einer Klasse, besser aufgehoben.
    Entweder in der Klasse, in der auch die Callback-Methoden hinterlegt sind (wie z. B. einem Formular)
    oder separat in einer Klasse, die eine vergleichbare Lebensdauer hat (z. B. einem Controller beim MVC).

    Wenn BackgroundWorker und die Callback-Methoden in der gleichen Instanz verwaltet werden, riskiert man nicht,
    dass er auf Dauer im Speicher verbleibt, weil noch Verweise über die Delegaten bestehen, siehe z. B.
    Events and Memory Leaks in .NET

    Gruß Elmar

    <sup>[1]</sup> Wenn Du bezüglich AsyncOperation an einem Exkurs interessiert bist, solltest Du Dir Mike Perez Artikelserie durchlesen:

    was hilfreich zum Verständnis des notwendigen Aufwandes ist. Und nicht nur beim Backgroundworker
    sondern auch bei Control.Invoke für die threadübergreifenden Zugriff zum Einsatz kommt.

    <sup>[2]</sup> Eine "richtige" Komponente ist er nicht, da er nicht den passenden Konstruktor (mit IContainer) implementiert,
    um in die components (eines Formulars) eingetragen zu werden - erkennbar daran, dass Site null ist.
    Und so gilt: Auch in einem Formular eingebettet, wird das Dispose nicht aufgerufen, da er in deren components
    nicht eingetragen wird. (nobugz Aussage bezüglich Dispose trifft auf ihn also nicht zu).
    Was als weiterer Beleg dienen kann, dass Dipose für einen Backgroundworker keinerlei Bedeutung hat.

     

    Dienstag, 5. April 2011 09:03
    Beantworter
  • Hallo Elmar,

    WOW! Da hast du dir mal wieder richtig Mühe gegeben!!!!! Vielen Dank!!!!!!

     

    Es ist sehr schön mal wieder was von dir zu hören! ;)

     

    Viele Grüße
    Holger M. Rößler

    Dienstag, 5. April 2011 18:05