线程处理模型

本文摘要:

1:理解与UI相关的多线程操作;
2:多个窗口多个线程
3:WPF中的多线程异常

1:理解与UI相关的多线程操作

    首先来说说传统Winform。我们知道传统Winform新起工作线程,在工作线程中不能对界面元素进行操作。如下面的代码,运行会报错“线程间操作无效: 从不是创建控件“label1”的线程访问它。”:

  
  
    
    
Thread t = new Thread( delegate ()
{
label1.Text
= " temp " ;
});
t.Start();

     要使上面的代码能成功运行,我们需要使用控件的InvokeBeginInvoke和方法。这两个方法的意思是说,让赋值这个行为交给UI线程去处理。代码如下:

  
  
代码
     
     
Thread t = new Thread( delegate ()
{
label1.Invoke(
new MethodInvoker( delegate ()
{
label1.Text
= " temp " ;
}));
});
t.Start();

  而WPF的控件,我们找不到InvokeBeginInvoke这两个方法了。因为WPF的UI线程都交给一个叫做调度器的类了。

     WPF 应用程序启动时具有两个线程:一个用于处理呈现,另一个用于管理 UI。 呈现线程实际上隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码。UI 线程在一个名为 Dispatcher 的对象中将工作项进行排队。 Dispatcher 根据优先级选择工作项,并运行每一个工作项直到完成。Dispatcher 类提供两种注册工作项的方法:InvokeBeginInvoke。 这两个方法都会安排执行一个委托。Invoke 是同步调用,即它直到 UI 线程实际执行完该委托时才返回。BeginInvoke 是异步调用,因而将立即返回。

     上面的代码在WPF中的实现如下:


   
   
代码
      
      
Thread t = new Thread( new ThreadStart( delegate
{
tb_test.Dispatcher.Invoke(
new Action( delegate
{
tb_test.Text
= " 123 " ;
}),
null );
}));
t.Start();

     注意,WPF中已经没有MethodInvoker这个类,我们使用Action代替。当然,你也可以使用自定义的委托声明。


2:多个窗口多个线程

    一些 WPF 应用程序需要多个顶级窗口。 一个线程/Dispatcher 组合管理多个窗口完全可以接受,但有时使用多个线程更佳。 特别是在其中一个窗口有可能独占线程时,采用多个线程的优点更为突出。

    Windows 资源管理器即采用这种工作方式。 每个新的资源管理器窗口都属于原始进程,但每个此类窗口都是在一个独立线程的控制下创建的。该例子的简单模仿如下代码:

 

代码
   
   
private void NewWindowHandler( object sender, RoutedEventArgs e)
{
Thread newWindowThread
= new Thread( new ThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground
= true ;
newWindowThread.Start();
}

private void ThreadStartingPoint()
{
Window1 tempWindow
= new Window1();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}

3:WPF中的多线程异常

多线程的异常处理,要采用特殊的做法。以下的处理方式会存在问题: 

 

代码
   
   
try
{
Thread t
= new Thread((ThreadStart) delegate
{
throw new Exception( " 多线程异常 " );
});
t.Start();
}
catch (Exception error)
{
MessageBox.Show(error.Message
+ Environment.NewLine + error.StackTrace);
}

 

应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会导致应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的还是当前线程的异常,而t是属于新起的异常,所以,正确的做法应该是: 

 

代码
   
   
Thread t = new Thread((ThreadStart) delegate
{
try
{
throw new Exception( " 多线程异常 " );
}
catch (Exception error)
{
MessageBox.Show(
" 工作线程异常: " + error.Message + Environment.NewLine + error.StackTrace);
}
});
t.Start();

 

也就是说,新起的线程中异常的捕获,可以将线程内部代码全部try起来。原则上来说,每个线程自己的异常应该在自己的内部处理完毕,不过仍旧有一个办法,可以将线程内部的异常传递到主线程。

 

在WPF窗体程序中,你可以采用如下的方法将工作线程的异常传递到主线程:

 

代码
   
   
Thread t = new Thread((ThreadStart) delegate
{
try
{
throw new Exception( " 非窗体线程异常 " );
}
catch (Exception ex)
{
this .Dispatcher.Invoke((Action) delegate
{
throw ex;
});
}
});
t.Start();

 

WPF窗体程序的处理方式与Windows窗体程序比较,有两个很有意思的地方:

 

第一个是,在Windows窗体中,我们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引发主线程的Application.ThreadException。而在WPF窗体程序中,无论是调度器的Invoke还是BeginInvoke方法都能将异常传递给主线程。

 

第二个地方就是InnerException。WPF的工作线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工作线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工作线程异常的Message。

 

 

转自:http://www.cnblogs.com/luminji/archive/2011/02/07/1949626.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值