Dispatcher中Invoke与BeginInvoke
[同步]Invoke
Application.Current.Dispatcher.Invoke(AutoIncreaseNumber);
[异步]BeginInvoke
Application.Current.Dispatcher.BeginInvoke((Action)AutoIncreaseNumber);
两者都会阻塞UI线程
基于WPF4.5.1示例
Invoke 按钮对应的是InvokeCommand
BeginInvoke按钮对应的是BeginInvokeCommand
可以发现,在执行按钮的命令时,UI线程是会阻塞,计时器并不会走动
1 public class MainViewModel : ViewModelBase 2 { 3 public MainViewModel() 4 { 5 DispatcherTimer timer = new DispatcherTimer(); 6 timer.Interval = TimeSpan.FromSeconds(1); 7 timer.Tick += timer_Tick; 8 timer.Start(); 9 } 10 11 void timer_Tick(object sender, EventArgs e) 12 { 13 Now = DateTime.Now; 14 } 15 16 private DateTime now = DateTime.Now; 17 18 public DateTime Now 19 { 20 get { return now; } 21 set 22 { 23 now = value; 24 RaisePropertyChanged("Now"); 25 } 26 } 27 28 29 30 private int number; 31 /// <summary> 32 /// 数值用于显示 33 /// </summary> 34 public int Number 35 { 36 get { return number; } 37 set 38 { 39 number = value; 40 RaisePropertyChanged("Number"); 41 } 42 } 43 44 private bool isIncrease; 45 /// <summary> 46 /// 是否可以自增长 47 /// </summary> 48 public bool IsIncrease 49 { 50 get { return isIncrease; } 51 set 52 { 53 isIncrease = value; 54 RaisePropertyChanged("IsIncrease"); 55 } 56 } 57 58 /// <summary> 59 /// 自动增长 60 /// </summary> 61 private void AutoIncreaseNumber() 62 { 63 IsIncrease = !isIncrease; 64 while (IsIncrease && Number < 500000) 65 { 66 Number++; 67 } 68 } 69 70 #region RelayCommands 71 /// <summary> 72 /// Invoke命令 73 /// </summary> 74 public RelayCommand InvokeCommand 75 { 76 get 77 { 78 return new RelayCommand(() => 79 { 80 Application.Current.Dispatcher.Invoke(AutoIncreaseNumber); 81 }); 82 } 83 } 84 /// <summary> 85 /// BeginInvoke命令 86 /// </summary> 87 public RelayCommand BeginInvokeCommand 88 { 89 get 90 { 91 return new RelayCommand(() => 92 { 93 //这里直接使用匿名方法会报错 94 //'Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type' 95 //使用强制转换的方式 96 Application.Current.Dispatcher.BeginInvoke((Action)AutoIncreaseNumber); 97 }); 98 } 99 } 100 /// <summary> 101 /// 清理数字命令 102 /// </summary> 103 public RelayCommand ClearCommand 104 { 105 get 106 { 107 return new RelayCommand(() => 108 { 109 Number = 0; 110 IsIncrease = false; 111 }); 112 } 113 } 114 #endregion 115 }
注:其中阻塞UI线程的原因是把Number的递增放到了Dispather中去执行,如果想要不阻塞,那么需要有一个新的DispatcherTimer的对象去执行这个递增的逻辑,那么就不会阻塞UI线程了。
所以说这里所说的异步并不是相对于UI线程的异步,那么究竟是什么?
Invoke 是同步操作;因此,直到回调返回之后才会将控制权返回给调用对象。
BeginInvoke 是异步操作;因此,调用之后控制权会立即返回给调用对象。
--- msdn
做一个测试
1 /// <summary> 2 /// DiffInInvokeAndBeginInvoke.xaml 的交互逻辑 3 /// </summary> 4 public partial class DiffInInvokeAndBeginInvoke : Window 5 { 6 public DiffInInvokeAndBeginInvoke() 7 { 8 InitializeComponent(); 9 this.ltb.ItemsSource = _infos; 10 this.Loaded += DiffInInvokeAndBeginInvoke_Loaded; 11 } 12 13 private ObservableCollection<string> _infos = new ObservableCollection<string>(); 14 15 private void DiffInInvokeAndBeginInvoke_Loaded(object sender, RoutedEventArgs e) 16 { 17 ExcuteMethod(); 18 ExcuteMethod_2(); 19 ExcuteMethod_3(); 20 } 21 22 private void ExcuteMethod() 23 { 24 Dispatcher.Invoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.SystemIdle, "1-1: SystemIdle Invoke"); 25 Dispatcher.Invoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "1-2: Send Invoke "); 26 Dispatcher.BeginInvoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Normal, "1-3: Normal BeginInvoke"); 27 Dispatcher.BeginInvoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "1-4: Send BeginInvoke"); 28 DispatcherOperation dop = Dispatcher.BeginInvoke(new Action<string>(PrintInformation), "1-5: Defaut BeginInvoke"); 29 } 30 31 private void ExcuteMethod_2() 32 { 33 34 Dispatcher.BeginInvoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Normal, "2-1: Normal BeginInvoke"); 35 Dispatcher.Invoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "2-2: Send Invoke "); 36 Dispatcher.BeginInvoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "2-3: Send BeginInvoke"); 37 } 38 39 private void ExcuteMethod_3() 40 { 41 Dispatcher.Invoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "3-1: Send Invoke "); 42 Dispatcher.BeginInvoke(new Action<string>(PrintInformation), System.Windows.Threading.DispatcherPriority.Send, "2-2: Send BeginInvoke"); 43 } 44 45 private void PrintInformation(string info) 46 { 47 _infos.Add(info); 48 } 49 }
结果如下:
从结果及MSDN对于Invoke及BeginInvoke的解释,很容易就理解了。
Invoke一定要执行完了才会执行下去,而BeginInvoke是没有等待执行完就接着往下走了,然后会根据线程的调用优先级开始执行。