none
Multithreading mit ProgressBar RRS feed

  • Frage

  • Hallo Zusammen,

    ich versuche ein Multithreading zu starten während meine Datenbank Daten per TableAdapter läd oder speichert.

    Dies Funktioniert soweit auch ganz gut, bis auf das die ProgressBar während des Lade/Speichervorganges nicht aktualisiert bzw einfriert.

    Hier Mein Code:
    Ich habe ein Window das die ProgressBar enthält.
    Des weiteren eine Methode die einen zweiten Thread startet, der per Delegate aufgerufen wird.

    In MainWindow:

    Window1 w = new Window1();
      private delegate void startProgBarHandler();
      private Thread scndThread = null;
    

     

     

    Die scndThread Main-Methode und die ProgBar start Methode

     private void scndThreadMain()
      {
       startProgBarHandler startBar = new startProgBarHandler(startProgBar);
       while ((Thread.CurrentThread.ThreadState & System.Threading.ThreadState.Running) == System.Threading.ThreadState.Running)
        {
         this.Dispatcher.Invoke(startBar);
        Thread.Sleep(1000);
       }
      }
    
    private void startProgBar()
      {   
       this.w.progBar.IsIndeterminate = true;
      }
    

     

     

     

    Und der Aufruf beim Speichern:

     private void btn_SaveData_Click(object sender, RoutedEventArgs e)
      { 
       /* Instanzieren des Datasets */
       LIMSDataSet lIMSDataSet = ((LIMSDataSet)(this.FindResource("lIMSDataSet")));
    
       MessageBoxResult result = System.Windows.MessageBox.Show("Sind Sie sicher, dass Sie die Änderungen in die Datenbank schreiben möchten?", 
        "Änderungen Übernehmen?", MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
       if (result == MessageBoxResult.OK)
       {
        this.w.Show();
        this.scndThread = new Thread(new ThreadStart(scndThreadMain));
        this.scndThread.Start();
        try
        {
         /* Speichern der Daten mit dem Table Adapter */
         this.lIMSDataSettbl_ConditionAsReceivedTableAdapter.Update(lIMSDataSet);
         
         /* Accept Changes führt zur Annahme des geänderten Zustandes 
          * im DataSet. Änderungen können nicht mehr per GetChanges() abgerufen werden. */
         lIMSDataSet.AcceptChanges();     
         System.Windows.MessageBox.Show("Daten wurden in die Datanbank geschrieben!", "Erfolg! ", 
    MessageBoxButton.OK, MessageBoxImage.Information); this.w.Close(); this.scndThread.Abort(); } catch { this.w.Close(); this.scndThread.Abort(); System.Windows.MessageBox.Show("Fehler beim Speichern! Daten wurden nicht gespeichert!", "Speicherproblem!", MessageBoxButton.OK, MessageBoxImage.Error); } } }

    Eigentlich handelt es sich doch schon um einen zweiten Thread, weshalb reagiert die ProgressBar nicht wie gewollt?

     

     


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)

    Sonntag, 14. August 2011 12:37

Antworten

  • Hallo Peter,

    Threads solltest du eigentlich nicht mehr direkt verwenden, da mit dem Backgroundworker und der TPL (Task parallel library) deutlich bessere und auch leistungsfähigere Alternativen zur Verfügung stehen.

    ich denke, dass der Backgroundworker die bessere Alternative für die wäre, da er unter anderem das ProgressReporting direkt unterstützt.

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


    Kaum macht man es richtig, schon funktioniert es
    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 13:24
  • Hallo Peter,

    auch bei WPF gilt, dass man auf die Oberfläche nur über den GUI Thread zugreifen darf, siehe Threading-Modell.

    Deswegen schau Dir mal das Beispiel in dem Thread Wartefenster an.

    Die Progressbar ist dort auf Dauerbetrieb geschaltet. weil es kein zählbares Ereignis gab.
    Willst Du sie mit diskreten Werte beschicken wäre beim DataAdapter ein Einklinken in das RowUpdated Ereignis möglich -
    bei einer oder einigen wenigen Zeile(n) wird man aber kaum was davon haben.

    BTW: Das AcceptChanges in Deinem Code ist unnötig (passiert bei Erfolg im Standard automatisch)
    und im Fehlerfalle sogar schädlich, da damit nichtgespeicherte Änderungen übernommen werden.

    Gruß Elmar

    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 15:24
  • Hi Peter,

    zus. zu den Hinweisen von Holger und Elmar - ich habe in diesem Thread ein simples Beispiel für einen BackgroundWorker und einen Progressbar gepostet - evt. hilft's ja.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 15:30

Alle Antworten

  • Hallo Peter,

    Threads solltest du eigentlich nicht mehr direkt verwenden, da mit dem Backgroundworker und der TPL (Task parallel library) deutlich bessere und auch leistungsfähigere Alternativen zur Verfügung stehen.

    ich denke, dass der Backgroundworker die bessere Alternative für die wäre, da er unter anderem das ProgressReporting direkt unterstützt.

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


    Kaum macht man es richtig, schon funktioniert es
    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 13:24
  • Hallo Peter,

    auch bei WPF gilt, dass man auf die Oberfläche nur über den GUI Thread zugreifen darf, siehe Threading-Modell.

    Deswegen schau Dir mal das Beispiel in dem Thread Wartefenster an.

    Die Progressbar ist dort auf Dauerbetrieb geschaltet. weil es kein zählbares Ereignis gab.
    Willst Du sie mit diskreten Werte beschicken wäre beim DataAdapter ein Einklinken in das RowUpdated Ereignis möglich -
    bei einer oder einigen wenigen Zeile(n) wird man aber kaum was davon haben.

    BTW: Das AcceptChanges in Deinem Code ist unnötig (passiert bei Erfolg im Standard automatisch)
    und im Fehlerfalle sogar schädlich, da damit nichtgespeicherte Änderungen übernommen werden.

    Gruß Elmar

    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 15:24
  • Hi Peter,

    zus. zu den Hinweisen von Holger und Elmar - ich habe in diesem Thread ein simples Beispiel für einen BackgroundWorker und einen Progressbar gepostet - evt. hilft's ja.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Als Antwort markiert Peter Sypek Montag, 15. August 2011 13:32
    Sonntag, 14. August 2011 15:30
  • Hi Peter,

    zus. zu den Hinweisen von Holger und Elmar - ich habe in diesem Thread ein simples Beispiel für einen BackgroundWorker und einen Progressbar gepostet - evt. hilft's ja.


    Cheers,
    Olaf
    http://blogs.intuidev.com

    Hallo Olaf, habe Dein Beispiel nachgestellt.

    Leider ohne den gewünschten Erfolg.

    Zwar wird die Progressbar aktiviert. Aber während die Datenbank die Daten bezieht steht der Fortschrittsbalken.

     


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)
    Montag, 15. August 2011 13:26
  • Hallo Peter,

    auch bei WPF gilt, dass man auf die Oberfläche nur über den GUI Thread zugreifen darf, siehe Threading-Modell.

    Deswegen schau Dir mal das Beispiel in dem Thread Wartefenster an.

    Die Progressbar ist dort auf Dauerbetrieb geschaltet. weil es kein zählbares Ereignis gab.
    Willst Du sie mit diskreten Werte beschicken wäre beim DataAdapter ein Einklinken in das RowUpdated Ereignis möglich -
    bei einer oder einigen wenigen Zeile(n) wird man aber kaum was davon haben.

    BTW: Das AcceptChanges in Deinem Code ist unnötig (passiert bei Erfolg im Standard automatisch)
    und im Fehlerfalle sogar schädlich, da damit nichtgespeicherte Änderungen übernommen werden.

    Gruß Elmar


    Vielen Dank Elmar,

    das werde ich mir nochmal ansehen.

    Beim Versuch das Ganze nachzustellen, hatte ich auf Anhieb probleme.

    Die Progressbar wird aktiveirt, aber das LadeDaten Delegate wird nicht ausgeführt.

    Dort hatte ich meinen Datenbezug aus der DB untergebracht.


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)
    Montag, 15. August 2011 13:29
  • Hallo Peter,

    Threads solltest du eigentlich nicht mehr direkt verwenden, da mit dem Backgroundworker und der TPL (Task parallel library) deutlich bessere und auch leistungsfähigere Alternativen zur Verfügung stehen.

    ich denke, dass der Backgroundworker die bessere Alternative für die wäre, da er unter anderem das ProgressReporting direkt unterstützt.

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


    Kaum macht man es richtig, schon funktioniert es


    Also mit Threads kenn ich mich noch so gut wie überhaupt nicht aus.

    Meine Lektüre spricht auch von "kompliziertes Modell, sollte in jedem Fall ausführlich studiert werden".

    Na dann muss ich da wohl durch!


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)
    Montag, 15. August 2011 13:31
  • Hi Peter,

    bist Du nun doch klargekommen ..?

    Zur Info - für das Aktualisieren des PBs bist natürlich Du verantwortlich. Wenn Deine Anwendung z.B. Daten in 10 Schritten ausliest (z.B. nacheinander aus 10 Tabellen) würdest Du den PB in 10%-Schritten aufteilen. Nach jedem Abruf der Daten aus einer Tabelle würdest Du die ReportProgress-Methode des BackgroundWorkers aufrufen, damit der PB das an den Benutzer berichtet. Ein wie auch immer geartetes - automatisches - Aktualisieren des PBs gibt's da nicht.

    Analog zum vorgenannten Beispielverhalten kannst Du das im genannten Thread gepostete Beispiel sehen - dort wären das dann halt 100 Tabellen, aus denen Daten geladen werden.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Montag, 15. August 2011 13:39
  • Hallo Peter,

    mir geht es wie Olaf:
    Ich sehe hier zwar viel Grünes, aber hast Du Dein Problem wirklich gelöst?

    Ich meinte im übrigen eher das vollständigere Beispiel, das ich zum Download bereitgestellt hatte.
    Wenn Du Schwierigkeiten hast, es auf einen TableAdapter / DataSet umzustellen,
    so bin ich gerne bereit das Beispiel darauf anzupassen.

    Gruß Elmar

    Montag, 15. August 2011 16:04
  • Entschuldigung ich war ein paar Tage verhindert.

    Ich werde mir die Beispiele nochmal genau ansehen,

    Bei Gelegenheit komme ich auf das Angebot mit der Anpassung zurück.

     

    Danke sehr!


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)
    Mittwoch, 17. August 2011 12:16
  • Hallo Peter,

     

    ich möchte auch mal meinen Senf dazu abgeben :)

    Mein Beispiel hat folgenden Hintergund: aus einer Tabelle werden Adressen geholt, um dann die Entfernungen zu einer angegebenen PLZ automatisch zu ermitteln. Hierbei soll für jede ausgewertete Adresse die ProgressBar weiterlaufen. Das ganze habe ich wie folgt gelöst:

     

    Die Schleife im Fenster:

    //...
    
        foreach (DataRow dr in dt.Rows)
        {
         // Abfrage der Entfernung...
         string adr = string.Format("{1} {2}, {0}", dr["strasse"], dr["plz"], dr["ort"]);
         // Adresse vorhanden?
         if ((dr["plz"].ToString() != "" && dr["plz"] != null) || (dr["ort"].ToString() != "" && dr["ort"] != null))
         {
          // Übergabeparameter für "Hintergrund"-Thread erstellen
          orte = new Dictionary<string, object>()
             {
              {"plz", tbxPLZ.Text},
              {"adr", adr},
              {"row", dr}
             };
          // ProgressBar aktualisieren
          pgbMAchecked.Value += 1;
          // km errechnen und ProgressBar aktualisieren
          Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(DoCheck));
         }
         else
          dr["km"] = "999";
        }
        // sortierte DataView an DataGrid binden
        DataView dv = dt.DefaultView;
        dv.Sort = "km";
        dgrMitarbeiter.DataContext = dv;
    
    //...
    

    (Hinweis: das Dictionary "orte" ist in der Klasse deklariert, um u.a. auch im Hintergrund-Thread drauf zugreifen zu können.)


    Die "Hintergrund"-Methode, die die eigentliche Arbeit verrichtet:

     

      void DoCheck()
      {
       int entfernung;
       iConnOk = MapService.HoleKm(orte["adr"].ToString(), orte["plz"].ToString(), out entfernung, iConnOk);
       (orte["row"] as DataRow)["km"] = entfernung; 
      }
    
    
    (Hinweis: "MapService" ist eine eigene statische Methode, die per WebClient eine Goodle-Maps-Anfrage sendet und auswertet.)

     

    Vielleicht kannst du damit ja auch was anfangen...

     

    Gruß

    Thomas


    Mittwoch, 17. August 2011 13:33
  • Hallo Peter,

    mir geht es wie Olaf:
    Ich sehe hier zwar viel Grünes, aber hast Du Dein Problem wirklich gelöst?

    Ich meinte im übrigen eher das vollständigere Beispiel, das ich zum Download bereitgestellt hatte.
    Wenn Du Schwierigkeiten hast, es auf einen TableAdapter / DataSet umzustellen,
    so bin ich gerne bereit das Beispiel darauf anzupassen.

    Gruß Elmar


    Also ich bin nicht wirklich weiter gekommen.
    Hier mein Versuch!

     public MainWindow()
     {
      InitializeComponent();
      bW.DoWork += LoadAllData; // Event-Handler für das laden der Daten
      bW.RunWorkerCompleted += LoadAllDataCompleted; // Event-Handler für das beenden des Ladevorgangs
      // Daten-Lade Thread starten
      bW.RunWorkerAsync(5000); // Start des seperaten Thread (Background Worker) max. 5s lang versuchen 
     }
    

    private void LoadAllData(object sender, System.ComponentModel.DoWorkEventArgs e)
     {
      // Progress Bar auf ständigen Betrieb setzen
      progBar.IsIndeterminate = true;
      EN_Test_Ampel.LIMSDataSet lIMSDataSet = ((EN_Test_Ampel.LIMSDataSet)(this.FindResource("lIMSDataSet")));
      // Hier schimpft VS Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet.
      
     // Hier kommen zahlreiche TableAdapter Fill(DataSet) methoden
    
      // Setzen der StatusBar Bachricht
      if (StatusBarMessage.Content == null)
      StatusBarMessage.Content = "Keine Fehler!";  
     }
    

    void LoadAllDataCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
      {
      // Progress Bar ausschalten
      progBar.IsIndeterminate = false;
      }
    

    Ich muss dazu sagen, dass ich mich mit Multithreading noch gar nicht auskenne!
    Und der Umgang mit Delegates läuft bei mir auch noch nicht soooo flüssig.

     

     


    Gruß

    Peter

    Wer grob fahrlässige Rechtschreibfehler findet, darf diese behalten ;0)


    Donnerstag, 18. August 2011 12:22