VS2010是经常阻塞UI线程的应用程序之一。例如用vs2010打开一个包含数百个项目的解决方案,可以要等待很长时间(感觉像卡死),自从vs2012情况得到了改善,项目在后台进行了异步加载。
一、同步模式
二、异步模式
三、基于事件的异步模式
四、基于任务的异步模式
4.5以后更新基于任务的异步模式。
1.最基本的任务
普通方法
public string A() { return "abc"; }
(1)基于任务的模式带有 Async后缀的方法,并返回一个Task类型。
public Task<string> AAsync() { return Task.Run(() => A()); }
其中Task<string>是一个返回字符串的任务。
用Task.Run方法在线程池上执行一个方法,并返回一个任务对象。
(2)使用关键字await来调用返回的任务 ,await关键字需要用带 Async修饰符的方法声明.
public async void BAsync() { string str = await AAsync(); Console.WriteLine("abc"); }
Async修饰符只能用于返回Task<>或者void的方法。而且Async不能修饰Main。await只能返回Task的方法
完整代码,执行一个异步任务
例1
class Program { static void Main(string[] args) { Program p = new Program(); p.BAsync(); Console.ReadKey(); } public string A() { return "abc"; } public Task<string> AAsync() { return Task.Run(() => A()); } public async void BAsync() { string str = await AAsync(); Console.WriteLine("abc"); } }
任务完成后执行
class Program { static void Main(string[] args) { Program p = new Program(); p.C(); Console.ReadKey(); } public string A() { return "abc"; } public Task<string> AAsync() { return Task.Run(() => A()); } public void C() { //用task对象接收返回对象 Task<string> t1 = AAsync(); //任务完成后调用的代码 t1.ContinueWith(t => { Thread.Sleep(3000); string s = t.Result; Console.WriteLine(s); }); Console.WriteLine("efg"); } }
(3)使用await async关键字,当await完成之后,不需要进行任何特别处理,就能访问UI线程
???
(4)使用多个异步方法
按顺序调用异步
修改例1 的BAsync()方法
public async void BAsync() { string str = await AAsync(); string str2 = await AAsync() + "d"; Console.WriteLine("{0},{1}",str,str2); }
如果一个异步依赖另一个异步方法的结果,await非常有用,如果完全独立,每个异步方法都不使用await,整个方法将更快返回结果
组合器
一个组合器可以接受多个同一类型的参数,并返回同一类型的值。
public async void BAsync() { Task<string> T1 = AAsync(); Task<string> T2 = AAsync(); //等待,直到两个任务完成,返回task await Task.WhenAll(T1,T2); Console.WriteLine("{0},{1}", T1.Result, T2.Result); //也可用于返回数组 string[] res= await Task.WhenAll(T1, T2); Console.WriteLine("{0},{1}", res[0], res[1]); //其中一个完成就返回task await Task.WhenAny(T1, T2); }
(5)转换异步模式
2.错误处理
3.取消
五、委托进行异步编程
1.BeginInvoke()
异步启动委托, 参数与要执行的方法的参数相同,另加两个可选参数
第一个参数是一个 AsyncCallback 委托,此委托引用在异步调用完成时要调用的方法。
第二个参数是一个用户定义的对象,该对象将信息传递到回调方法。
BeginInvoke 将立即返回,而不会等待异步调用完成。
BeginInvoke 返回可用于监视异步调用的进度的 IAsyncResult。
IAsyncResult 对象 =委托对象.BeginInvoke(定义委托时有几个参数,AsyncCallback ,类的对象);
2.EndInvoke()
EndInvoke 方法用于检索异步调用的结果,它可以在调用 BeginInvoke之后的任意时间调用。
如果异步调用尚未完成,那么 EndInvoke 将阻止调用线程,直到完成异步调用。
这种方式非常适合执行文件或网络操作。
(1)使用 EndInvoke 等待异步调用
在控制台下运行
新建了一个测试类,一个用来同步,一个用来异步。
class Program { public delegate string delegateClass(int time); static void Main(string[] args) { TextClass textClass = new TextClass(); //把要执行的异步方法存入委托 delegateClass testMethod = new delegateClass(textClass.asynMethod); //启动异步委托,参数一有没有取决于定义委托时的参数有没有 IAsyncResult result = testMethod.BeginInvoke(3000, null, null); //当异步委托结束后,把该委托返回的值回传给str string str1 = testMethod.EndInvoke(result); Console.WriteLine(str1); Console.WriteLine("..............."); //运行同步方法 string str2 = textClass.synMethod(0); Console.WriteLine(str2); Console.ReadKey(); } } //测试类 class TextClass { //用来同步 public string synMethod(int time) { Console.WriteLine("启动同步方法"); Thread.Sleep(time); Console.WriteLine("结束同步方法"); return "同步返回"; } //用来异步 public string asynMethod(int time) { Console.WriteLine("启动异步方法"); Thread.Sleep(time); Console.WriteLine("结束异步方法"); return "异步返回"; } }
运行结果:
发现结果异步操作执行后,才执行的同步操作。
然后注释掉“等待异步调用的方法”
//string str1 = testMethod.EndInvoke(result); //Console.WriteLine(str1);
在运行:
EndInvoke()方法会把所在的线程卡住(阻塞)
所以如果在winfrom下运行EndInvoke(),在异步未执行完的条件下会把界面卡死。EndInvoke方法不能和用户界面在同一个线程下。
(2)对异步调用的完成情况进行轮询
public delegate string delegateClass(int time); static void Main(string[] args) { TextClass textClass = new TextClass(); //把要执行的异步方法存入委托 delegateClass testMethod = new delegateClass(textClass.asynMethod); //启动异步委托 IAsyncResult result = testMethod.BeginInvoke(3000, null, null); //如果异步为执行完成,会不断循环执行此方法。 while (result.IsCompleted == false) { //Thread.Sleep(350); Console.Write("a"); } //当异步委托结束后,把该委托返回的值回传给str string str1 = testMethod.EndInvoke(result); Console.WriteLine(str1); Console.ReadKey(); } } //测试类 class TextClass { //用来异步 public string asynMethod(int time) { Console.WriteLine("启动异步方法"); Thread.Sleep(time); Console.WriteLine("b"); Console.WriteLine("结束异步方法"); return "异步返回"; } }
结果
(3)异步调用完成时执行回调方法
winfrom
public partial class Form1 : Form { public Form1() { InitializeComponent(); //是否捕获对错误线程的调用 Control.CheckForIllegalCrossThreadCalls = false; } // private delegate string delegatehWeb(); delegatehWeb delWeb=new delegatehWeb(webReuest); private void button1_Click(object sender, EventArgs e) { //执行异步,异步结束后,执行Callback方法 IAsyncResult result = delWeb.BeginInvoke(Callback, null); textBox1.Text = "HTML代码"; } //回调方法 public void Callback(IAsyncResult result) { string str = delWeb.EndInvoke(result); textBox1.Text = str; //MessageBox.Show(str); } //获取网站HTML public static string webReuest() { HttpWebRequest httpWebRequest = WebRequest.CreateHttp("http://www.baidu.com/"); WebResponse httpWebResponse = httpWebRequest.GetResponse(); //返回数据流 Stream stream = httpWebResponse.GetResponseStream(); Encoding encode = Encoding.GetEncoding("utf-8"); StreamReader readStream = new StreamReader(stream, encode); //把流转成字符串 string htmlStr = readStream.ReadToEnd(); Thread.Sleep(3000); return htmlStr; } }
task 执行异步任务, t.Wait()阻塞所在线程
private void button1_Click(object sender, EventArgs e)
{
Task t = Task.Run(() => {
Thread.Sleep(3000);
//所在线程id MessageBox.Show(Convert.ToString(Thread.CurrentThread.ManagedThreadId)); }); MessageBox.Show(Convert.ToString(Thread.CurrentThread.ManagedThreadId)); //等待task完成后向后执行 t.Wait(); MessageBox.Show(Convert.ToString(Thread.CurrentThread.ManagedThreadId)); }