关于异步编程模式
关于异步编程模式
注:以下内容适用于WPF、C#编码。
最近对异步编程产生了较大的兴趣,所以写出来和感兴趣的朋友一起分享!
1、关于Dispatcher 调度类:
提供用于管理线程的工作项队列的服务。
通常,WPF 应用程序从两个线程开始:一个用于管理 UI,一个用于处理呈现。 - UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码; - 呈现线程有效地隐藏在后台运行。
UI 线程在一个名为 Dispatcher 的调度对象内部对工作项进行排队。调度 Dispatcher基于优先级选择工作项,并运行每一个工作项直到完成。
WPF 中的大多数类都派生自 DispatcherObject。DispatcherObject 在构造时存储一个当前线程的 Dispatcher 的引用。
通常在单独的线程中处理大型操作,而专门让 UI 线程来处理 Dispatcher 队列中的工作项。当大型操作完成时,可以通知 UI 线程来显示。
如果只有一个线程可以修改 UI,那么后台线程如何与用户交互? 后台线程可以请求 UI 线程执行,通过向 UI 线程的 Dispatcher 类提供两个方法:
Invoke 和 BeginInvoke。两个方法均调度一个委托来执行。
- Invoke (请求)是同步调用,也就是说,直到 UI 线程实际执行完该委托后才返回。 - BeginInvoke 是异步的,将立即返回。
(调用“BeginInvoke”方法,则公共语言运行库 (CLR) 将对请求进行排队并立即返回到调用方,由ThreadPool 线程池的线程调用该方法。
BeginInvoke 返回 IasyncResult,可用于监视调用进度。如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。)
Dispatcher 按优先级对其队列进行排序。向 Dispatcher 队列中添加元素时可指定 10 个级别。这些优先级在 DispatcherPriority 枚举中维护。
2、同步方法和异步方法的区别:
同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果。
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作。
3、异步执行模式优点:
--异步执行方式使应用程序能摆脱单个任务的牵制,提高了灵活性和应用程序的执行效率。 --虽然使用异步执行模式在编程序时十分复杂,但可以实现多任务、并行执行,使执行的效率大大提高。
例如查询操作,客户机上的应用程序在向服务器发出了查询操作的指令后,将立刻执行查询语句指令的下一条语句,而不需要等到服务器将查询结果返回客户机端。
4、异步交互的优先级:
- systemidle:枚举值是1 。业务处理时,该系统处于闲置状态。
- ApplicationIdle: 枚举值是2 。业务处理时,该应用处于闲置状态。(空闲时调用,常用!!)
- Normal:枚举值是9 。业务处理正常优先。这是典型的应用优先权。(正常调用,常用!!) - Send:枚举值是10 。在处理其他异步业务之前执行。这是最高优先。
异步编程方法:共3中方式。
一、同一线程方式:
public delegate void NextPrimeDelegate(); startStopButton.Dispatcher.BeginInvoke( DispatcherPriority.Normal,
new NextPrimeDelegate(CheckNextNumber)); public void CheckNextNumber() {
// Reset flag. NotAPrime = false; // If a prime number. if (!NotAPrime) {
//因为在同一个线程中,所以可以直接更新TextBox bigPrime.Text = num.ToString(); }
// 系统空闲时间执行
startStopButton.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.SystemIdle, new NextPrimeDelegate(this.CheckNextNumber)); }
二、独立线程方式:———重点!!!
private delegate void NoArgDelegate();
private delegate void OneArgDelegate(String arg); // 用委托的异步交互方式,在新的单独的线程工作,重点!!
NoArgDelegate fetcher = new NoArgDelegate(this.FetchWeatherFromServer); fetcher.BeginInvoke(null, null); // Launch thread private void FetchWeatherFromServer() {
Random rand = new Random(); String weather; if (rand.Next(2) == 0) {
weather = \"rainy\"; } else {
weather = \"sunny\"; }
this.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal, new OneArgDelegate(UpdateUserInterface), weather); }
注:使用的是线程池中的线程,有数量限制, 建议采用新建线程的方式更好一些!!如下:
Thread myTest = new Thread(new ThreadStart(FetchWeatherFromServer)); myTest.Start();
实测结果:采用线程池约1ms,新建线程约10ms,创建时间上差不多,但新建线程对UI响应速度比线程池更快!!!!
三、多个窗口,多个线程方式:
using System.Threading;
private void Btn_Click (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(); }
当单击“new window”(新建窗口)按钮时,在一个新线程中,新建一个窗口,并以异步方式启动。
在此线程的控制下创建一个新窗口。WPF 自动创建一个新的 Dispatcher 来管理新线程。要使此窗口工作,只需启动 Dispatcher。
注:
另外:下面的方式与方式一相同,为同一线程方式!! private delegate void dg();
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new System.Threading.ThreadStart(new dg(Test))); 感想:
因为单线程单任务的程序执行效率较低,通过使用多线程并行处理来显著的提高执行效率。 从程序的复杂度、安全性和执行效率方面考虑,微软在WPF中采用了3种方法提高效率: 1、不起线程,只在原来的线程中,有效的利用UI用户响应的空闲时间进行运算(顺序执行)——最简单;
2、使用线程池中的线程——系统开销小,安全性较高; 3、新起线程——复杂度最高,但效率最高。
附MSDN上的示例:
1、具有长时间运行计算的单线程应用程序示例
2、 通过调度程序模拟天气预报服务的示例 3、 多线程 Web 浏览器示例
因篇幅问题不能全部显示,请点此查看更多更全内容