【WPF】Invoke与BeginInvoke的区别

概述

在WPF多线程编程中,经常要在工作线程中更新界面显示,Invoke和BeginInvoke即是为了解决此类问题。在WPF多线程编程模型中,通过Dispatcher调度程序,来管理UI工作项队列,并拥有应用程序主线程,在大多数WPF应用程序中,只存在一个用户界面线程和一个调度器。.

在非UI线程更新UI,通过调用Invoke和BeginInvoke来实现,那么两者有什么区别呢?

不同点

Invoke是同步更新,会阻塞所在工作者线程,BeginInvoke是异步更新,不会阻塞当前线程。

Invoke调用后将指定代码立即插入主线程中执行,而BeginInvoke调用后,发送消息给UI线程,相当于向Dispatcher队列中添加工作项,待之前UI更新任务完成后,再执行BeginInvoke中的内容。

在使用场景上,Invoke方法会拖延线程直到执行完指定代码,如果需要暂停异步操作,直到用户提供反馈信息,可使用Invoke,比如指定代码弹出YES/NO对话框,需要根据用户反馈以进一步执行操作的场景。

相同点:两者虽然一个为同步,一个为异步,实际是运行在同一线程,即UI主线程。

代码示例

void ButtonClick(object sender, RoutedEventArgs e)
{
    //在UI线程(主线程)中调用Invoke和BeginInvoke(一般不这样用,没有实际意义)
    this.Dispatcher.Invoke(() =>
     {
         this.ContentTextBlock.Text += "Main thread Invoke and thread id :" + Thread.CurrentThread.ManagedThreadId + '\n';
     });
  //这里会阻塞UI
     this.Dispatcher.BeginInvoke(new Action(delegate
     {
         this.ContentTextBlock.Text += "Main thread BeginInvoke and thread id :" + Thread.CurrentThread.ManagedThreadId + '\n';
     }));
     //在工作者线程中调用Invoke和BeginInvoke
    Task.Factory.StartNew(() =>
    {
        this.Dispatcher.Invoke(() =>
        {
            this.ContentTextBlock.Text += " Worker thread Invoke and thread id :" + Thread.CurrentThread.ManagedThreadId + '\n';
        });
    //这里会阻塞UI
        this.Dispatcher.BeginInvoke(new Action(delegate
        {
            this.ContentTextBlock.Text += " Worker thread BeginInvoke and thread id :" + Thread.CurrentThread.ManagedThreadId + '\n';
        }));
    });
}
//输出结果
Main thread Invoke and thread id : 10
Main thread BeginInvoke and thread id : 10
Worker thread Invoke and thread id : 10
Worker thread BeginInvoke and thread id : 10

参考:
1. Control的Invoke和BeginInvoke
2. Invoke 和 BeginInvoke 的真正涵义