Bearbeiten von Steuerelementen aus Threads
Frank Dzaebel, erstellt am: 19.11.2005, zuletzt geändert:
12.06.2010
Kategorie:Multithreading, .NET-Version:1.1-4.0
Methoden, die sich auf Steuerelemente auswirken, sollten nur aus dem Thread ausgeführt werden, auf dem das Steuerelement erstellt wurde.
Das .NET Framework stellt Methoden bereit, die von jedem beliebigen Thread aus ohne Sicherheitsrisiko aufgerufen werden können und die ihrerseits Methoden aufrufen, die mit Steuerelementen anderer Threads interagieren. Die Control.Invoke-Methode
(bzw.
Dispatcher.Invoke
siehe WPF unter .NET 3.x) ermöglicht die synchrone Ausführung von Methoden für Steuerelemente. Ein paar zentrale Info-Links zum Thema und Kurz-Beispielen zu [anonymes
Delegate, benanntes Delegate,
BackgroundWorker und WPF].
|
MSDNMAG 07 |
, |
WPF-Threads: Erstellen reaktionsfähigerer Anwendungen mit dem Dispatcher -- MSDN
Magazine |
|
MSDN 2007 |
, |
Webcast: Advanced WPF (Teil 5-6) - Mulithreading
|
|
MSDN 2005 |
, |
Bearbeiten von Steuerelementen aus Threads |
|
MSDN 2005 |
, |
Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads |
|
J. Albahari '08 |
, |
Threading in C# - Free E-book [PDF] |
|
22.06.2004 |
, |
Sicheres und einfaches Multithreading in Windows Forms, (Part1) / Part 2
/ Part 3 |
|
21.06.2004 |
, |
Ein zweiter Blick auf Windows Forms-Multithreading |
|
MSDN 2005 |
, |
Control.InvokeRequired-Eigenschaft |
|
MSDN 2006 |
, |
Gewusst wie: Threadsicheres Aufrufen von Windows Forms-Steuerelementen |
|
10.10.2005 |
, |
Was jeder Entwickler über Multithread-Anwendungen wissen muss |
|
29.10.2005 |
, |
Auswirkungen von Low-Lock-Techniken in Multithread-Anwendungen |
|
18.01.2006 |
, |
C# Tipps, Teil 1 - Threads, Prozesse und Synchronisierung |
|
J. Rogers '04 |
, |
WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequired |
|
25.09.2007 |
, |
BackgroundWorker Threads and Supporting Cancel |
|
Synchronisierung |
|
|
|
12.4.2010 |
, |
Threading (C# und Visual Basic) /
Threadsynchronisierung (C# und
Visual Basic) |
|
1.11.2003 |
, |
Synchronisieren des Zugriffs auf eine freigegebene Ressource ... |
|
1.04.2006 |
, |
Avoiding And Detecting Deadlocks In .NET Apps with C# and C++ |
|
06.2009 / 04.2010 |
, |
CHESS - Microsoft Research - Detect "data
races", "dead locks", etc. /
Disciplined Concurrency Testing
|
1) Kurzlösung: Mit Invoke über anonymes Delegate:
Wenn Sie beispielsweise in einer Quellcodezeile:
txtInfo.Text =
"meine Info";
folgende Fehlermeldung: "Ungültiger threadübergreifender Vorgang" (Illegal
Cross Thread Operation) bekommen,
ersetzen Sie die Zeile (zum Beispiel) einmal mit:
txtInfo.Invoke(new Action<string>(s=>{txtInfo.Text
= s;}), "meine Info");
2) Mit Invoke, über benanntes
delegate
public partial class Form1 : Form
{
private TextBox textBox1;
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1 = new TextBox(); textBox1.Width = 150; Controls.Add(textBox1);
new Thread(new ThreadStart(ThreadMethode)).Start();
}
private int counter = 0;
public void ThreadMethode()
{
while (counter < 500)
{
textBox1.Invoke(new UpdateTextCallback(this.UpdateText),
"aus Nicht-UI-Thread:" + (counter++).ToString());
Thread.Sleep(5);
}
}
public delegate void UpdateTextCallback(string text);
private void UpdateText(string text)
{
textBox1.Text = text;
}
}
3) Ohne Invoke, mit BackgroundWorker
private TextBox textBox1;int counter = 0;
BackgroundWorker bw;
private void Form1_Load(object sender,EventArgs e)
{ textBox1 =new TextBox(); textBox1.Width = 150;
Controls.Add(textBox1); bw =new BackgroundWorker();
bw.WorkerReportsProgress =true;
bw.DoWork +=new DoWorkEventHandler(bw_DoWork); bw.RunWorkerAsync();
bw.ProgressChanged +=new ProgressChangedEventHandler(bw_ProgressChanged);
}
void bw_ProgressChanged(object sender,ProgressChangedEventArgs e)
{ textBox1.Text ="aus UI-Thread:" + (counter).ToString();
}
void bw_DoWork(object sender,DoWorkEventArgs e)
{
while (counter < 500)
{ bw.ReportProgress(counter++ / 500);
Thread.Sleep(5);//Nicht notwendig, nur künstliche Verlangsamung
}
}
4) WPF: Mit
Dispatcher.Invoke
und delegate in WPF
[Infos]
Wenn Sie Timer nutzen wollen, tuen Sie dies am einfachsten über den
DispatcherTimer, denn der ist automatisch immer im "richtigen" Thread (in dem das DispatcherTimer-Objekt
erzeugt wurde). Andere Timer würden ggf. einen Thread aus dem Threadpool benutzen, der dann ggf. erst
über Invoke auf den richtigen Thread umgeleitet werden müsste.
Beachten Sie, daß
Invoke wartet,
wenn der Erstellungs-Thread in irgendeiner Weise blockiert
ist. Eine Lösung kann
BeginInvoke sein. Die
BackgroundWorker Komponente kann mit WPF weiterhin problemlos eingesetzt
werden. Weitere Stichwörter sind DispatcherObject,
Freezable,
IsFrozen, etc..
Window1.xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title ="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<Canvas Name="cvs">
<TextBox Name="textBox1">Anfangs-Text</TextBox>
</Canvas>
</Window>
Window1.xaml.cs:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ThreadStart start = delegate()
{
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<string>(SetzeStatus), "aus anderem Thread");
};
new Thread(start).Start();
}
void SetzeStatus(string meldung)
{
textBox1.Text = meldung;
}