![]() |
Threads und BackgroundWorker
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:
Wie synchronisiere ich das thread-safe?
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"); } Und wie funktioniert diese BackgroundWorker-Komponente? ich habe da nicht wirklich was hilfreiches gefunden, was ich verstehen würde. |
Re: Threads und BackgroundWorker
Synchronisieren zum Beispiel so (häufig z.B. in einem Ereignishandler anzutreffen, der auf ein im Thread ausgelöstes Ereignis reagiert):
Code:
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.
void thread1_Event(object sender, EventArgs e)
{ if (InvokeRequired) { Invoke(new EventHandler(thread1_Event)); } else { // Mach, was du eigentlich machen willst } } 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. |
Re: Threads und BackgroundWorker
Synchronisation mache ich am liebsten über Monitore. Schau dir mal die Klasse Monitor an - das Konzept ist das gleiche wie bei Java z.B..
|
Re: Threads und BackgroundWorker
Wie benutzt du denn die Monitore?
Code:
ist ja gleichbedeutend mit
lock (x)
{ DoSomething(); }
Code:
Von daher ist lock im Normalfall ja einfacher und weniger fehleranfällig anzuwenden.
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj); try { DoSomething(); } finally { System.Threading.Monitor.Exit(obj); } |
Re: Threads und BackgroundWorker
Zitat:
In diesem Fall bieten sich entweder der Thread-Pool oder Asynchronous Delegates an:
Code:
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.
// 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); } |
Re: Threads und BackgroundWorker
Also ich habe es jetzt so gelöst:
Code:
Mit Monitore kenne ich mich auch in Java nicht aus.
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); } } 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? |
Re: Threads und BackgroundWorker
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. |
Re: Threads und BackgroundWorker
Zitat:
Code:
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.
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); } } |
Re: Threads und BackgroundWorker
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 ;) |
Re: Threads und BackgroundWorker
Zitat:
OT: Wenn ich das Leerzeichen weglasse, wird der Block unten zwar erkannt, erscheint dann aber in Times New Roman :stupid: Zitat:
Zitat:
Code:
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.
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(); } Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:33 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz