myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
   » Plugin für Firefox
   » Plugin für IE7
   » Gadget für Vista
» Regeln
» Wie poste ich richtig?
» Datenschutzerklärung
» wbb-FAQ

Mitglieder
» Liste / Suche
» Stadt / Anleitung dazu
» Wer ist wo online?

Angebote
» ASP.NET Webspace
» Bücher
» Zeitschriften
   » dot.net magazin
» Accessoires

Ressourcen
» .NET-Glossar
» guide to C#
» openbook: Visual C#
» openbook: OO
» .NET BlogBook
» MSDN Webcasts
» dotnetjob.de
» Search.Net

Team
» Kontakt
» Übersicht
» Wir über uns
» Bankverbindung
» Impressum

» Unsere MiniCity
MiniCity
» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Basistechnologien und allgemeine .NET-Klassen » BackgroundWorker, MMTimer & GUI
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | An Freund senden | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

BackgroundWorker, MMTimer & GUI

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Maddinel Maddinel ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-3180.gif


Dabei seit: 08.03.2004
Beiträge: 1.365
Entwicklungsumgebung: VS 2010 Express Editions
Herkunft: Deutschland


Maddinel ist offline

BackgroundWorker, MMTimer & GUI

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hi,

ich habe folgende Situation:

In meiner Anwendung lasse ich einen MultimediaTimer laufen um sehr zeitgenau und regelmäßig eine Aktion durchführen zu können (der Standard-Timer war hierfür nicht genau genug). Um die Timerausführung auch bei weiteren Aktionen in der GUI auf keinen Fall zu stören, lagere ich die Erzeugung und das Tick-Event über einen Backgroundworker in einen zweiten Thread aus. Das funktioniert soweit auch so wie gedacht.

Und jetzt zum Problem bei der Sache:

Ich muss die Ausführung des TickEvents in der GUI anzeigen. Gemäß  [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) habe ich mir also gedacht, dass das ProgressChanged-Event des BackgroundWorkers genau das ist was ich suche. Leider klappt das nicht. Da ich im Worker die Klasse nur erzeuge, in der dann der MMTimer gestartet wird, bekomme ich beim Aufruf von ProgressChanged im Tick-Event dann die Meldung "Für diesen Vorgang wurde OperationCompleted bereits aufgerufen. Weitere Aufrufe sind unzulässig."

Wie komme ich aus dieser Misere?
11.06.2012 20:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Darth Maim Darth Maim ist männlich
myCSharp.de-Mitglied

Dabei seit: 23.10.2009
Beiträge: 199
Entwicklungsumgebung: Visual Studio 2010


Darth Maim ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Maddinel:
Wie komme ich aus dieser Misere?

Indem du den Backgroundworker weglässt oder im BackgroundWorker DoWork event so lange was machst, bis du den Timer nicht mehr brauchst. Aber der BackgroundWorker ist dafür eigentlich nicht gedacht.

Darth Maim
11.06.2012 20:39 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Maddinel Maddinel ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-3180.gif


Dabei seit: 08.03.2004
Beiträge: 1.365
Entwicklungsumgebung: VS 2010 Express Editions
Herkunft: Deutschland

Themenstarter Thema begonnen von Maddinel

Maddinel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Du meinst ich sollte lieber direkt mit "Thread" arbeiten?
11.06.2012 20:43 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ujr
myCSharp.de-Poweruser/ Experte

Dabei seit: 24.11.2007
Beiträge: 1.593


ujr ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Maddinel:
Das funktioniert soweit auch so wie gedacht.

Bisschen Code zum Verständnis wäre nicht schlecht. Was erzeugst Du wo und wie? Was hat der BackgroundWorker mit dem Multimedia-Timer zu tun?
11.06.2012 21:33 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Maddinel Maddinel ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-3180.gif


Dabei seit: 08.03.2004
Beiträge: 1.365
Entwicklungsumgebung: VS 2010 Express Editions
Herkunft: Deutschland

Themenstarter Thema begonnen von Maddinel

Maddinel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Bisschen Code zum Verständnis wäre nicht schlecht.

Ok, hier mal zum Verständnis:

C#-Code:
class Main
{
  BackgroundWorker worker = new BackgroundWorker();
  Working_Hard wh_ref;

  worker_DoWork()
  {
    Working_Hard wh = new Working_Hard()
    wh_ref = wh;
    wh.starte_timer();
  }

  button1_click()
  {
    worker.RunWorkerAsync();
  }

  button2_click()
  {
    wh_ref.verändere_parameter(parameter);
  }
}

class Working_Hard
{
  MMTimer timer = new MMTimer();

  starte_timer()
  {
    timer.Start();
  }

  timer_Tick()
  {
    //mache was zeitkritisches ...
  }

  verändere_parameter(parameter)
  {
    //Parameter ändern, die im Tick-Event verarbeitet werden ...
  }
}

Das ist es in groben Zügen. Im Hauptprogramm starte ich den Vorgang per button1, der dann im Hintergrund den Timer startet. Der wiederum wird im Hintergrund regelmäßig durchlaufen über das Tick-Event im zweiten Thread. Ich habe auch die Möglichkeit über meine Referenzklasse wh_ref dem Vorgang auch im Nachhinein noch andere Parameter, die z.B. durch den Benutzer eingegeben wurden, zu verändern.
11.06.2012 22:33 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
michlG michlG ist männlich
myCSharp.de-Team (Moderation)

images/avatars/avatar-2909.png


Dabei seit: 26.07.2007
Beiträge: 3.428
Entwicklungsumgebung: VS 2010 PRO
Herkunft: Naturns - Südtirol - Italien


michlG ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

das Problem ist dass deine DoWork Methode lange schon fertig ist bis du das Timer-Tick kriegst und damit dann den Progress festlegen willst
D.h. der BackgroundWorker hat seine Arbeit schon erledigt (DoWork ist fertig) und du willst später erst sagen. So jetzt habe ich 10% gemacht, was nicht sein kann da er schon fertig hat.

Irgendwie ist der gesamte Code irgendwie sehr schwierig überschaubar. Sind zwar nur ein paar Zeilen aber wirklich strukturiert wirkt das nicht.
Statt den BackgroundWorker kannst du einfach einen Thread verwenden. In deiner Working_Hard Klasse kannst du ein Event einbauen welches du schmeisst wenn sich der Fortschritt ändert. Dieses Event abonnierst du im MainWindow und gibst dann den Wert auf der GUI aus.
Du darfst dabei aber nicht das Invoken vergessen da du dich in einen anderen Thread befindest.

Grüße
Michael
11.06.2012 23:30 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Maddinel Maddinel ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-3180.gif


Dabei seit: 08.03.2004
Beiträge: 1.365
Entwicklungsumgebung: VS 2010 Express Editions
Herkunft: Deutschland

Themenstarter Thema begonnen von Maddinel

Maddinel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
das Problem ist dass deine DoWork Methode lange schon fertig ist bis du das Timer-Tick kriegst und damit dann den Progress festlegen willst

Ja genau. Der BW dient hier quasi nur zum Einstieg in den zweiten Thread. Die Thread-Variante macht wohl wirklich deutlich mehr Sinn. Ich werde mir das mal entsprechend umbauen und mit eigenem Event umsetzen.
Danke!
11.06.2012 23:41 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Darth Maim Darth Maim ist männlich
myCSharp.de-Mitglied

Dabei seit: 23.10.2009
Beiträge: 199
Entwicklungsumgebung: Visual Studio 2010


Darth Maim ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das Problem ist, dass nachdem du den Timer gestartet hast (also nach wh.starte_timer()), die DoWork Methode vorbei ist und damit der BackgroundWorker nicht mehr läuft. Dann kannst du natürlich nicht mehr die ProgressChanged Methode aufrufen.

Aber da meiner Meinung nach das tick event sowieso nicht im GUI, bzw. dem Thread, der den Timer gestartet hast, brauchst du das doch alles garnicht mit dem BackgroundWorker.

Also im Grunde sowas:

C#-Code:
class Program {
    private WorkingHard _wh;

    private void startWork() {
        _wh = new WorkingHard();
        _wh.Start();
        _wh.StatusUpdate += (o,e) => this.Invoke(new Action<object, WorkingHardEventArgs>(wh_StatusUpdate), o, e);
    }

    private void updateParameters(Foo bar) {
        _wh.updateParameter(bar);
    }

    private void wh_StatusUpdate(object sender, WorkingHardEventArgs e) {
        this.progressBar.Value = e.Percentage;
    }
}

class WorkingHard {
    MMTimer _timer = new MMTimer();

    public void Start() {
        _timer.Start();
    }

    private void Tick() {
        //Do something

        OnStatusUpdate(42);
    }

    private void OnStatusUpdate(int percentage) {
        if(StatusUpdate != null) StatusUpdate(percentage);
    }

    public event EventHandler<WorkingHardEventArgs> StatusUpdate;
}

class WorkingHardEventArgs : EventArgs {
    public WorkingHardEventArgs(int percentage) {
        _percentage = percentage;
    }

    private int _percentage = 0;
    public int Percentage { get { return _percentage; } }
}

Falls das Tick-Event des MMTimers doch im gleichen Thread ausgeführt wird, in dem er erstellt wurde, mach die Start-Methode einfach so:

C#-Code:
ThreadPool.QueueUserWorkItem(o => _timer.Start());

Darth Maim
11.06.2012 23:49 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ujr
myCSharp.de-Poweruser/ Experte

Dabei seit: 24.11.2007
Beiträge: 1.593


ujr ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Darth Maim:
Aber da meiner Meinung nach das tick event sowieso nicht im GUI, bzw. dem Thread, der den Timer gestartet hast, brauchst du das doch alles garnicht mit dem BackgroundWorker.

Das ist richtig. Es gibt ggf. ein kleines Aber. Wenn es sich bei der MMTimer-Klasse um die Klasse von Codeproject handelt und ich mich recht erinnere, so hatte diese ein SynchronizationObject (o. ä.). Das darf man natürlich nicht benutzen (s. Quelltext). Vielleicht willst Du es aber auch gerade verwenden, um direkt auf die GUI zugreifen zu könnnen?

Interessant wäre dann noch, wie lange Deine "zeitkritische" Operation gegenüber dem Intervall des Timers dauert?
12.06.2012 11:05 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Maddinel Maddinel ist männlich
myCSharp.de-Mitglied

images/avatars/avatar-3180.gif


Dabei seit: 08.03.2004
Beiträge: 1.365
Entwicklungsumgebung: VS 2010 Express Editions
Herkunft: Deutschland

Themenstarter Thema begonnen von Maddinel

Maddinel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@michlG:
Perfekt! So klappts. Ich habs jetzt per Thread umgesetzt und den Backgroundworker komplett rausgeschmissen. Das Problem der Aktualisierung löse ich über ein in die Worker-Klasse eingebautes Event, das ich im Hauptprogramm abonniere. Im Eventhandler Invoke ich die zu verwendeten Elemente des Main-Threads und schwups ... es klappt so wie ich das wollte! Danke noch mal!

@Darth Maim:
Stimmt, ich hatte es zu Anfang auch genau so (ohne zweiten Thread) genau so umgesetzt, da ich auch so dachte. Das Problem ist aber hierbei, dass das Tick-Event selbst zar in einem zweiten Thread läuft, aber der Aufruf des Tick-Events wird trotzdem ausgebremst, wenn das Hauptprogramm beschäftigt ist.
15.06.2012 00:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 11 Monate.
Der letzte Beitrag ist älter als 11 Monate.
Antwort erstellen


© Copyright 2003-2013 myCSharp.de-Team. Alle Rechte vorbehalten. 25.05.2013 05:19