AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Threads und BackgroundWorker

Ein Thema von Luckie · begonnen am 13. Mär 2008 · letzter Beitrag vom 14. Mär 2008
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#1

Threads und BackgroundWorker

  Alt 13. Mär 2008, 11:10
Ich versuche gerade rauszufinden, wie man mit Threads unter C# arbeitet. Anscheinend gibt es da ja zwei Möglichkeiten, entweder mit dem Thread-Objekt oder der BackgroundWorker Komponente.

Ich würde gerne beides verstehen und benutzen können. Nur leider scheitere ich schon bei der Thread-Klasse:
Code:
private void ThreadProc()
        {
            lock (this.listBox1)
            {
                for (int i = 0; i < 100; i++)
                    listBox1.Items.Add("Thread");
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(ThreadProc);
            t.Start();    
            for (int i = 0; i < 100; i++)
                listBox1.Items.Add("main");
        }
Wie synchronisiere ich das thread-safe?

Und wie funktioniert diese BackgroundWorker-Komponente? ich habe da nicht wirklich was hilfreiches gefunden, was ich verstehen würde.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
OregonGhost

Registriert seit: 8. Jun 2002
Ort: Lübeck
1.216 Beiträge
 
Delphi 3 Professional
 
#2

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 11:21
Synchronisieren zum Beispiel so (häufig z.B. in einem Ereignishandler anzutreffen, der auf ein im Thread ausgelöstes Ereignis reagiert):
Code:
void thread1_Event(object sender, EventArgs e)
{
    if (InvokeRequired) {
        Invoke(new EventHandler(thread1_Event));
    } else {
        // Mach, was du eigentlich machen willst
    }
}
Nicht schön, aber geht. Dieser Eventhandler funktioniert dann von jedem Thread aus. Google findet auch noch die eine oder andere Klasse, die solche Dinge etwas vereinfacht.

Wenn du nicht auf die GUI zugreifen willst, ist lock() schon der richtige Ansatz zur Synchronisierung.
Edit: Gerade noch einmal über deinen Code geschaut. Du hast da was missverstanden: lock ist kein Äquivalent zu Delphis Synchronize. lock entspricht grob einer CriticalSection unter Windows, oder in allen anderen Threading-Bibliotheken einem Mutex. Damit sperrst du also den Zugriff auf ein Objekt, auf das nicht mehrere Objekte gleichzeitig zugreifen sollen. Synchronize macht hingegen im Prinzip ein Invoke.

Ach ja: Du kannst natürlich auch, anstatt umständlich zu prüfen etc., von vornherein vom Thread aus ein Invoke oder BeginInvoke ausführen. Das kommt dann am ehesten der Synchronize-Geschichte nahe.

BackgroundWorker funktioniert etwas anders, außer dem DoWork-Event werden alle Events automatisch im richtigen Thread ausgelöst. Vereinfacht ausgedrückt: Auf Form ziehen, im DoWork-Ereignis deine Dinge erledigen und das dann per RunWorkerAsync aufrufen. Die übrigen Ereignisse und Methoden sind zur zusätzlichen Kommunikation gedacht.
Oregon Ghost
---
Wenn NULL besonders groß ist, ist es fast schon wie ein bisschen eins.
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#3

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 13:19
Synchronisation mache ich am liebsten über Monitore. Schau dir mal die Klasse Monitor an - das Konzept ist das gleiche wie bei Java z.B..
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
OregonGhost

Registriert seit: 8. Jun 2002
Ort: Lübeck
1.216 Beiträge
 
Delphi 3 Professional
 
#4

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 13:28
Wie benutzt du denn die Monitore?
Code:
lock (x)
{
    DoSomething();
}
ist ja gleichbedeutend mit
Code:
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}
Von daher ist lock im Normalfall ja einfacher und weniger fehleranfällig anzuwenden.
Oregon Ghost
---
Wenn NULL besonders groß ist, ist es fast schon wie ein bisschen eins.
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#5

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 13:32
Zitat von OregonGhost:
Ach ja: Du kannst natürlich auch, anstatt umständlich zu prüfen etc., von vornherein vom Thread aus ein Invoke oder BeginInvoke ausführen. Das kommt dann am ehesten der Synchronize-Geschichte nahe.
Das würde ich auch empfehlen, dadurch habe ich InvokeRequired noch nie benötigt.

In diesem Fall bieten sich entweder der Thread-Pool oder Asynchronous Delegates an:
Code:
// ThreadPool
// Der Name sagt alles: anstatt jedes Mal einen neuen Thread zu
// erstellen, wird wenn möglich ein alter aus dem Pool benutzt.
// Kein Rückgabewert
// => Nach Ende noch einmal Invoke oder andersweitige Übergabe
ThreadPool.QueueUserWorkItem((state) => {
   int sum = 0;
   for (int i = 1; i <= 10005; i++) {
      sum += i;
      if (i % 1000 == 0)
          // Achtung: Invoke ist synchron
            // asynchron: BeginInvoke
            Invoke(new Action<int>(AddItem), sum);
            // oder inline:
            // Invoke(new Action(() => listBox.Items.Add(i)));
   }
   Invoke(new Action(() => listBox.Items.Add(sum)));
}, null);


 //3. Asynchronous Delegates
 //Benutzen intern ThreadPool
 //Rückgabewert möglich
Func<int> func =
    () => {
        int sum = 0;
        for (int i = 1; i <= 10005; i++) {
            sum += i;
            if (i % 1000 == 0)
                Invoke(new Action(() => listBox.Items.Add(sum)));
        }
        return sum;
    };

func.BeginInvoke(
    (IAsyncResult result) =>
      Invoke(new Action(() => AddItem(func.EndInvoke(result)))),
    null);

[...]

void AddItem(int i)
{
   listBox.Items.Add(i);
}
Wenn ich's recht bedenke, gibt es aber keinen wirklichen Vorteil der Delegates im Vergleich zum Thread-Pool, weshalb ich mich mit ihnen gar nicht erst auseinandersetzen würde.
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#6

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 14:15
Also ich habe es jetzt so gelöst:
Code:
private void thread1_Event(object sender, EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler(thread1_Event));
            }
            else
            {
                for (int i = 0; i < 1000; i++)
                {
                    this.listBox1.Items.Add("Thread" + i.ToString());
                    this.listBox1.Update();
                    this.Text = "Thread";
                    this.Update();
                    Thread.Sleep(0);            
                }
            }
        }

        private void ThreadProc()
        {
            thread1_Event(this, null);    
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread( ThreadProc);
            t.Start();
            t.Priority = ThreadPriority.BelowNormal;
            for (int i = 0; i < 1000; i++)
            {
                this.listBox1.Items.Add("Main" + i.ToString());
                this.listBox1.Update();
                this.Text = "Main";
                this.Update();
                Thread.Sleep(0);
            }
        }
Mit Monitore kenne ich mich auch in Java nicht aus.

Jetzt mus sich nur noch rausfinden, wie das mit dem BackgroundWorker funktioniert.

Wo ist denn da der Unterschied bzw. was sind Vor- und/oder Nachteile?
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
OregonGhost

Registriert seit: 8. Jun 2002
Ort: Lübeck
1.216 Beiträge
 
Delphi 3 Professional
 
#7

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 14:43
Der BackgroundWorker nimmt dir die ganze Arbeit der Verwaltung ab, wenn dein Thread nichts weiter macht als irgendwas zu arbeiten, seinen Fortschritt zu melden und schließlich zu sagen, dass er fertig ist. Das läuft Event-basiert und ist sehr einfach aufgebaut. Probier es mal aus, wenn du nicht mehr Informationen von deinen Threads brauchst, ist es vielleicht sogar besser als alles von Hand zu machen.

@InvokeRequired:
Was macht Invoke, wenn man schon im richtigen Thread ist? Es geht trotzdem den Umweg über die Nachrichtenschleife, oder? Mit manueller Prüfung per InvokeRequired kann man dann schnelleren Code schreiben, wenn dieser nicht thread-übergreifend aufgerufen wird, und der Thread muss nicht wissen, ob er die Funktion direkt ausführen darf oder nicht. Wie so oft, mehr Aufwand -> mehr Möglichkeiten.
Oregon Ghost
---
Wenn NULL besonders groß ist, ist es fast schon wie ein bisschen eins.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#8

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 14:53
Zitat von OregonGhost:
BackgroundWorker funktioniert etwas anders, außer dem DoWork-Event werden alle Events automatisch im richtigen Thread ausgelöst. Vereinfacht ausgedrückt: Auf Form ziehen, im DoWork-Ereignis deine Dinge erledigen und das dann per RunWorkerAsync aufrufen. Die übrigen Ereignisse und Methoden sind zur zusätzlichen Kommunikation gedacht.
Das habe ich jetzt mal gemacht:
Code:
private void button1_Click(object sender, EventArgs e)
        {
            if (rbThread.Checked)
            {
                Thread t = new Thread(ThreadProc);
                t.Start();
            }
            else
            {
                backgroundWorker1.RunWorkerAsync();
            }
            for (int i = 0; i < 1000; i++)
            {
                this.listBox1.Items.Add("Main" + i.ToString());
                this.listBox1.Update();
                this.Text = "Main";
                this.Update();
                Thread.Sleep(0);
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 1000; i++)
            {
                this.listBox1.Items.Add("BackgroundWorker" + i.ToString());
                this.listBox1.Update();
                this.Text = "BackgroundWorker";
                this.Update();
                Thread.Sleep(0);
            }
        }
Allerdings bekooe ich bei this.listBox1.Items.Add("BackgroundWorker" + i.ToString()); eine Fehlermeldung, dass ich versuche aus einem anderen Thread auf ein Steuerelement zu zugreifen.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
OregonGhost

Registriert seit: 8. Jun 2002
Ort: Lübeck
1.216 Beiträge
 
Delphi 3 Professional
 
#9

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 15:23
Ja. DoWork wird in einem separaten Thread ausgeführt. Aber du kannst von hier aus z.B. ReportProgress aufrufen und in deinem Formular auf das ProgressChanged-Ereignis reagieren.

PS: Psst, mit [ C][ /C] sieht der Code viel besser aus
Oregon Ghost
---
Wenn NULL besonders groß ist, ist es fast schon wie ein bisschen eins.
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#10

Re: Threads und BackgroundWorker

  Alt 13. Mär 2008, 17:05
Zitat von OregonGhost:
PS: Psst, mit [ C][ /C] sieht der Code viel besser aus
mit [csharp ] noch besser .
OT: Wenn ich das Leerzeichen weglasse, wird der Block unten zwar erkannt, erscheint dann aber in Times New Roman
Zitat von Luckie:
Also ich habe es jetzt so gelöst:
<schnipp />
Du führst die gesamte Schleife im Hauptthread aus, das ist glaube ich nicht so gedacht . Wobei sich das bei deinem Beispiel so oder so nicht verhindern lässt, denn es werden ja nur Controls manipuliert; der zweite Thread ist nichts anderes als zusätzlicher Overhead. Ist das ListBox-Befüllen denn das Ziel oder geht es um etwas Anderes?
Zitat von Luckie:
Jetzt mus sich nur noch rausfinden, wie das mit dem BackgroundWorker funktioniert.
So dürfte dein Beispiel aussehen:
Code:
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
   for (int i = 0; i < 1000; i++) {
      backgroundWorker.ReportProgress(42);
      Thread.Sleep(0);
   }
}

void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   listBox1.Items.Add("Main" + i.ToString());
   listBox1.Update();
   Text = "Main";
   Update();
}
Naja, wir haben im Vergleich zum Thread-Pool Invoke durch ReportProgress ersetzt, das war's wohl schon. Für mich ziehe ich da das gleiche Fazit wie bei den Delegates: Wenn ich mit der universellen Methode fast gleich wenig Arbeit habe wie mit der speziellen, dann bleibe ich lieber bei ersterer.

Zitat von OregonGhost:
@InvokeRequired:
Nehmen wir meinen Code von oben: dort ist doch überall erkennbar, ob ich mich in einem Thread befinde und ob ich deshalb AddItem in Invoke wrappen muss oder nicht. Warum sollte ich dann noch InvokeRequired in AddItem abfragen? Wenn man dann noch Form- und Thread-Code fein säuberlich trennt, kann es noch weniger Verwechslungen geben.
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz