新建SL4 应用程序,在MainPage下添加代码:
<Button x:Name="btnThread1" Click="btnThread1_Click">Thread1</Button>
后台代码为:
private void btnThread1_Click(object sender, RoutedEventArgs e) { new Thread(() => { MessageBox.Show("Hello World"); }).Start(); }
如果你运行程序,点击按钮,会得到下面的异常。
这个问题的原因很简单:一个线程尝试调用另一个线程的方法 解决这个异常的方式很简单,
1:使用DependencyObject.Dispatcher.BeginInvoke 方法:
private void btnThread1_Click(object sender, RoutedEventArgs e) { new Thread(() => { this.Dispatcher.BeginInvoke(() => { MessageBox.Show("Hello World"); }); }).Start(); }
2:使用SynchronizationContext 对象
private void btnThread1_Click(object sender, RoutedEventArgs e) { SynchronizationContext context = SynchronizationContext.Current; new Thread(() => { context.Post((state) => { MessageBox.Show("Hello World"); }, null); }).Start(); }
但是这两者都有一个缺陷,假设有多个线程,例如多线程的多线程:
private void btnThread1_Click(object sender, RoutedEventArgs e) { new Thread(() => { SynchronizationContext context = SynchronizationContext.Current; new Thread(() => { context.Post((state) => { MessageBox.Show("Hello World"); }, null); }).Start(); }).Start(); }
虽然这里保存了context,但是因为context并不是UI线程的SynchronizationContext,所以还是会跑出异常。
所以提出了第三种方案:
1:新建静态类UISynchronizationContext,代码如下:
/// <summary> /// UI线程的SynchronizationContext /// </summary> public static class UISynchronizationContext { public static SynchronizationContext Context { get; set; } }
修改App.Xaml.cs 代码的构造函数,在构造App的时候设置
UISynchronizationContext.Context = SynchronizationContext.Current; public App() { this.Startup += this.Application_Startup; this.Exit += this.Application_Exit; this.UnhandledException += this.Application_UnhandledException; //保存UI线程同步上小文 UISynchronizationContext.Context = SynchronizationContext.Current; InitializeComponent(); }
使用的时候只需要:
private void btnThread1_Click(object sender, RoutedEventArgs e) { new Thread(() => { new Thread(() => { UISynchronizationContext.Context.Post((state) => { MessageBox.Show("Hello World"); }, null); }).Start(); }).Start(); }
其实Silverlight 已经提供了相似功能的类了,它就是
System.Windows.Deployment
你完全可以将上面的代码修改为:
new Thread(() => { new Thread(() => { //UISynchronizationContext.Context.Post((state) => // { // MessageBox.Show("Hello World"); // }, null); System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show("Hello World"); }); }).Start(); }).Start();