UI线程块

时间:2011-09-23 11:44:21

标签: wpf multithreading deadlock dispatcher

我创建了一个简单的WPF应用程序,并在默认窗口中添加了一个按钮。当我点击按钮时,会调用一个模拟的长工作方法(使用Thread.Sleep(15000)进行模拟)。我试图让按钮异步执行,但是尽管有以下在线示例,按钮和整个窗口会立即锁定单击并保持不变,直到Thread.Sleep(...)结束。

为什么会发生这种情况?

以下是代码:

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

3 个答案:

答案 0 :(得分:13)

您将长操作放回UI线程中。让我评论你的例子:

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

所以,你应该像这样修改你的例子:

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

正如其他人所提到的,使用BackgroundWorker类更容易。这是一个例子:

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
}

答案 1 :(得分:3)

通过使用BeginInvoke,您实际上是在UI线程上执行代码。

您只需在从后台工作线程更新UI时使用此功能。如果您只是:

 Thread thread = new System.Threading.Thread(
     new System.Threading.ThreadStart(
      delegate()
      {
           Thread.Sleep(15000);
      }
    ));

我认为它会奏效。

但是,您没有提出“已完成工作”事件,因此您无法知道线程何时(或确实如此)已完成。查看BackgroundWorker class。这对你来说很重要。您只需将代码插入DoWork方法。

答案 2 :(得分:1)