衡水网站建立要多少钱,阿里云服务器创建多个网站,阿里云快速做网站,沈阳 商城 网站 开发前言在.Net程序开发过程中#xff0c;我们经常会遇到如下场景#xff1a;编写WinForm程序客户端#xff0c;需要查询数据库获取数据#xff0c;于是我们根据需求写好了代码后#xff0c;点击查询#xff0c;发现界面卡死#xff0c;无法响应。经过调试#xff0c;发现查… 前言在.Net程序开发过程中我们经常会遇到如下场景编写WinForm程序客户端需要查询数据库获取数据于是我们根据需求写好了代码后点击查询发现界面卡死无法响应。经过调试发现查询数据库这一步执行了很久在此过程中UI被阻塞无法响应任何操作。如何解决此问题我们需要分析问题成因在WinForm窗体运行时只有一个主线程即为UI线程UI线程在此过程中既负责渲染界面又负责查询数据因此在大量耗时的操作中UI线程无法及时响应导致出现问题。此时我们需要将耗时操作放入异步操作使主线程继续响应用户的操作这样可以大大提升用户体验。直接编写异步编程也许不是一件轻松的事和同步编程不同的是异步代码并不是始终按照写好的步骤执行且如何在异步执行完通知前序步骤也是其中一个问题因此会带来一系列的考验。幸运的是在.Net Framework中提供了多种异步编程模型以及相关的API这些模型的存在使得编写异步程序变得容易上手。随着Framework的不断升级相应的模型也在不断改进下面我们一起来回顾一下.Net异步编程的前世今生。第一个异步编程模型APM概述APM全称Asynchronous Programing Model顾名思义它即为异步编程模型最早出现于.Net Framework 1.x中。它使用IAsyncResult设计模式的异步操作一般由BeginOperationName和EndOperationName两个方法实现这两个方法分别用于开始和结束异步操作例如FileStream类中提供了BeginRead和EndRead来对文件进行异步字节读取操作。使用在程序运行过程中直接调用BeginOperationName后会将所包含的方法放入异步操作并返回一个IAsyncResult结果同时异步操作在另外一个线程中执行。每次在调用BeginOperationName方法后还应调用EndOperationName方法来获取异步执行的结果下面我们一起来看一个示例using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace APMTest
{class Program{public delegate void ConsoleDelegate();static void Main(string[] args){ConsoleDelegate consoleDelegate new ConsoleDelegate(ConsoleToUI);Thread.CurrentThread.Name 主线程Thread;IAsyncResult ar consoleDelegate.BeginInvoke(null, null);consoleDelegate.EndInvoke(ar);Console.WriteLine(我是同步输出我的名字是 Thread.CurrentThread.Name);Console.Read();}public static void ConsoleToUI(){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name 线程池Thread;}else{Thread.CurrentThread.Name 普通Thread;}Thread.Sleep(3000); //模拟耗时操作Console.WriteLine(我是异步输出我的名字是 Thread.CurrentThread.Name);}}
}在这段示例中我们定义了一个委托来使用其BeginInvoke/EndInvoke方法用于我们自定义方法的异步执行同时将线程名称打印出来用于区分主线程与异步线程。如代码中所示在调用BeginInvoke之后立即调用了EndInvoke获取结果那么会发生什么呢如下图所示看到这里大家也许会比较诧异为什么同步操作会在异步操作之后输出呢这样不是和同步就一样了吗原因是这样的EndInvoke方法会阻塞调用线程直到异步调用结束由于我们在异步操作中模拟了3s耗时操作所以它会一直等待到3s结束后输出异步信息此时才完成了异步操作进而进行下一步的同步操作。同时在BeginInvoke返回的IAynscResult中包含如下属性通过轮询IsCompleted属性或使用AsyncWaitHandle属性均可以获取异步操作是否完成从而进行下一步操作相关代码如下所示using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace APMTest
{class Program{public delegate void ConsoleDelegate();static void Main(string[] args){ConsoleDelegate consoleDelegate new ConsoleDelegate(ConsoleToUI);Thread.CurrentThread.Name 主线程Thread;IAsyncResult ar consoleDelegate.BeginInvoke(null, null);//此处改为了轮询IsCompleted属性AsyncWaitHandle属性同理while (!ar.IsCompleted){Console.WriteLine(等待执行...);}consoleDelegate.EndInvoke(ar);Console.WriteLine(我是同步输出我的名字是 Thread.CurrentThread.Name);Console.Read();}public static void ConsoleToUI(){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name 线程池Thread;}else{Thread.CurrentThread.Name 普通Thread;}Thread.Sleep(3000); //模拟耗时操作Console.WriteLine(我是异步输出我的名字是 Thread.CurrentThread.Name);}}
}运行后结果如下可以发现在轮询属性时程序仍然会等待异步操作完成进而进行下一步的同步输出无法达到我们需要的效果那么究竟有没有办法解决呢此时我们需要引入一个新方法使用回调。在之前的操作中使用BeginInvoke方法两个参数总是传入的为null。若要使用回调机制则需传入一个类型为AsyncCallback的回调函数并在最后一个参数中传入需要使用的参数如以下代码所示using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace APMTest
{class Program{public delegate void ConsoleDelegate();static void Main(string[] args){ConsoleDelegate consoleDelegate new ConsoleDelegate(ConsoleToUI);Thread.CurrentThread.Name 主线程Thread;//此处传入AsyncCallback类型的回调函数并传入需要使用的参数consoleDelegate.BeginInvoke(CallBack, consoleDelegate);//IAsyncResult ar consoleDelegate.BeginInvoke(null, null);此处改为了轮询IsCompleted属性AsyncWaitHandle属性同理//while (!ar.IsCompleted)//{// Console.WriteLine(等待执行...);//}//consoleDelegate.EndInvoke(ar);Console.WriteLine(我是同步输出我的名字是 Thread.CurrentThread.Name);Console.Read();}public static void ConsoleToUI(){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name 线程池Thread;}else{Thread.CurrentThread.Name 普通Thread;}Thread.Sleep(3000); //模拟耗时操作Console.WriteLine(我是异步输出我的名字是 Thread.CurrentThread.Name);}public static void CallBack(IAsyncResult ar){//使用IAsyncResult的AsyncState获取BeginInvoke中的参数并用于执行EndInvokeConsoleDelegate callBackDelegate ar.AsyncState as ConsoleDelegate;callBackDelegate.EndInvoke(ar);}}
}运行后结果如下此时可以看出使用回调的方式已经实现了我们需要的效果。在同步执行时将耗时操作放入异步操作从而不影响同步操作的继续执行在异步操作完成后回调返回相应的结果。小结APM模型的引入使得编写异步程序变的如此简单只需定义委托将要执行的方法包含其中并调用Begin/End方法对即可实现异步编程。在一些基础类库中也已经提供了异步操作的方法直接调用即可。同时我们可以看到BeginInvoke方法实际上是调用了线程池中的线程进行操作因此APM模型也应属于多线程程序同时包含主线程与线程池线程。但是APM模型也存在一些缺点若不使用回调机制则需等待异步操作完成后才能继续执行此时未达到异步操作的效果。在异步操作的过程中无法取消也无法得知操作进度。若编写GUI程序异步操作内容与主线程未在同一线程操作控件时会引起线程安全问题。为了解决这些缺陷微软推出了其他的异步模式预知后事如何且听下回分解。您的点赞和在看是我创作的最大动力感谢支持公众号wacky的碎碎念知乎wacky