UI Thread Block
I have created a simple WPF Application and added a button to the default window. When I click on the button, a simulated long working method(simulated using a Thread.Sleep(15000) is called. I am trying to make the button execute asynchronously how开发者_如何学Goever despite following online examples, the button and entire window locks as soon as I click and remains so until the Thread.Sleep(...) finishes.
Any ideas why this is happening?
Here is the code:
private void button1_Click(object sender, RoutedEventArgs e)
{
DoSomeAsyncWork();
}
private void DoSomeAsyncWork()
{
System.Windows.Threading.Dispatcher.Run();
Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Thread.Sleep(15000)));
}
));
thread.Start();
}
You are putting the long operation back into the UI thread. Let me comment your example:
Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate() {
// here we are in the background thread
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => {
// here we are back in the UI thread
Thread.Sleep(15000);
}));
}
));
So, you should modify your example like this:
Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate() {
// here we are in the background thread
Thread.Sleep(15000); // <-- do the long operation here
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => {
// here we are back in the UI thread
// do stuff here that needs to update the UI after the operation finished
}));
}
));
As others have mentioned, it's easier to use the BackgroundWorker class. Here's an example:
private void DoSomeAsyncWork()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, args) => {
// do your lengthy stuff here -- this will happen in a separate thread
Thread.Sleep(15000);
}
bw.RunWorkerCompleted += (sender, args) => {
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
// do any UI stuff after the long operation here
...
}
bw.RunWorkerAsync(); // start the background worker
}
By using BeginInvoke
you are actually executing the code on the UI thread.
You only need to use this when you are updating your UI from the background worker thread. If you simply have:
Thread thread = new System.Threading.Thread(
new System.Threading.ThreadStart(
delegate()
{
Thread.Sleep(15000);
}
));
I think it will work.
However, you aren't raising a "Work Completed" event so you have no way of knowing when (or indeed if) the thread has finished. Look into the BackgroundWorker
class. This does a lot of the heavy lifting for you. You just need to plug in your code to the DoWork
method.
You should use BackgroundWorker.
- http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
- http://www.tanguay.info/web/index.php?pg=codeExamples&id=232
精彩评论