UI Thread Block

fin picture fin · Sep 23, 2011 · Viewed 10.2k times · Source

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 however 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();
}

Answer

Heinzi picture Heinzi · Sep 23, 2011

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
}