C#异步的世界【上】

C#异步的世界【上】,第1张

概述新进阶的程序员可能对async、await用得比较多,却对之前的异步了解甚少。本人就是此类,因此打算回顾学习下异步的进化史。 本文主要是回顾async异步模式之前的异步,下篇文章再来重点分析async异步模式。APMAPM 异步编程模型,Asynchronous Programming Model早在C#1的时候就有了APM。虽然不是很熟悉,但是多少还是见过的。就是那些类是BeginXXX和EndXXX的方法,且BeginXXX返回值是IAsyncResult接口。在正式写APM示例之前我们先给出一段同步代码://1、同步方法private void button1_Click(object sender, EventArgs e){Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);var request = WebRequest.Create("https://github.com/");//为了更好的演示效果,我们使用网速比较慢的外网request.GetResponse();//发送请求Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);label1.Text = "执行完毕!";}【说明】为了更好的演示异步效果,这里我们使用winform程序来做示例。(因为winform始终都需要UI线程渲染界面,如果被UI线程占用则会出现“假死”状态)【效果图】看图得知:我们在执行方法的时候页面出现了“假死”,拖不动了。我们看到打印结果,方法调用前和调用后线程ID都是9(也就是同一个线程)下面我们再来演示对应的异步方法:(BeginGetResponse、EndGetResponse所谓的APM异步模型)private void button2_Click(object sender, EventArgs e){//1、APM 异步编程模型,Asynchronous Programming Model//C#1[基于IAsyncResult接口实现BeginXXX和EndXXX的方法]Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);var request = WebRequest.Create("https://github.com/");request.BeginGetResponse(new AsyncCallback(t =>//执行完成后的回调{var response = request.EndGetResponse(t);var stream = response.GetResponseStream();//获取返回数据流using (StreamReader reader = new StreamReader(stream)){StringBuilder sb = new StringBuilder();while (!reader.EndOfStream){var content = reader.ReadLine();sb.Append(content);}Debug.WriteLine("【Debug】" + sb.ToString().Trim().Substring(0, 100) + "...");//只取返回内容的前100个字符Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);label1.Invoke((Action)(() => { label1.Text = "执行完毕!"; }));//这里跨线程访问UI需要做处理}}), null);Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);}【效果图】 看图得知:启用异步方法并没有是UI界面卡死异步方法启动了另外一个ID为12的线程上面代码执行顺序:前面我们说过,APM的BebinXXX必须返回IAsyncResult接口。那么接下来我们分析IAsyncResult接口:首先我们看:确实返回的是IAsyncResult接口。那IAsyncResult到底长的什么样子?:并没有想象中的那么复杂嘛。我们是否可以尝试这实现这个接口,然后显示自己的异步方法呢?首先定一个类MyWebRequest,然后继承IAsyncResult:(下面是基本的伪代码实现)public class MyWebRequest : IAsyncResult{public object AsyncState{get { throw new NotImplementedException(); }}public WaitHandle AsyncWaitHandle{get { throw new NotImplementedException(); }}public bool CompletedSynchronously{get { throw new NotImplementedException(); }}public bool IsCompleted{get { throw new NotImplementedException(); }}}这样肯定是不能用的,起码也得有个存回调函数的属性吧,下面我们稍微改造下:然后我们可以自定义APM异步模型了:(成对的Begin、End)public IAsyncResult MyBeginXX(AsyncCallback callback){var asyncResult = new MyWebRequest(callback, null);var request = WebRequest.Create("https://github.com/");new Thread(() => //重新启用一个线程{using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream())){var str = sr.ReadToEnd();asyncResult.SetComplete(str);//设置异步结果}}).Start();return asyncResult;//返回一个IAsyncResult}public string MyEndXX(IAsyncResult asyncResult){MyWebRequest result = asyncResult as MyWebRequest;return result.Result;}调用如下:private void button4_Click(object sender, EventArgs e){Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);MyBeginXX(new AsyncCallback(t =>{var result = MyEndXX(t);Debug.WriteLine("【Debug】" + result.Trim().Substring(0, 100) + "...");Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);}));Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);}效果图:我们看到自己实现的效果基本上和系统提供的差不多。启用异步方法并没有是UI界面卡死异步方法启动了另外一个ID为11的线程【总结】个人觉得APM异步模式就是启用另外一个线程执行耗时任务,然后通过回调函数执行后续 *** 作。APM还可以通过其他方式获取值,如:while (!asyncResult.IsCompleted)//循环,直到异步执行完成 (轮询方式){Thread.Sleep(100);}var stream2 = request.EndGetResponse(asyncResult).GetResponseStream();或asyncResult.AsyncWaitHandle.WaitOne();//阻止线程,直到异步完成 (阻塞等待)var stream2 = request.EndGetResponse(asyncResult).GetResponseStream(); 补充:如果是普通方法,我们也可以通过委托异步:(BeginInvoke、EndInvoke)public void MyAction(){var func = new Func<string, string>(t =>{Thread.Sleep(2000);return "name:" + t + DateTime.Now.ToString();});var asyncResult = func.BeginInvoke("张三", t =>{string str = func.EndInvoke(t);Debug.WriteLine(str);}, null);}EAPEAP 基于事件的异步模式,Event-based Asynchronous Pattern此模式在C#2的时候随之而来。先来看个EAP的例子:private void button3_Click(object sender, EventArgs e){Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);BackgroundWorker worker = new BackgroundWorker();worker.DoWork += new DoWorkEventHandler((s1, s2) =>{Thread.Sleep(2000);Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);});//注册事件来实现异步worker.RunWorkerAsync(this);Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);} 【效果图】(同样不会阻塞UI界面)【特征】通过事件的方式注册回调函数通过 XXXAsync方法来执行异步调用例子很简单,但是和APM模式相比,是不是没有那么清晰透明。为什么可以这样实现?事件的注册是在干嘛?为什么执行RunWorkerAsync会触发注册的函数?感觉自己又想多了...我们试着反编译看看源码: 只想说,这么玩,有意思吗?TAPTAP 基于任务的异步模式,Task-based Asynchronous Pattern到目前为止,我们觉得上面的APM、EAP异步模式好用吗?好像没有发现什么问题。再仔细想想...如果我们有多个异步方法需要按先后顺序执行,并且需要(在主进程)得到所有返回值。首先定义三个委托:public Func<string, string> func1(){return new Func<string, string>(t =>{Thread.Sleep(2000);ret

新进阶的程序员可能对async、await用得比较多,却对之前的异步了解甚少。本人就是此类,因此打算回顾学习下异步的进化史。 

APM

APM 异步编程模型,Asynchronous Programming Model

早在C#1的时候就有了APM。虽然不是很熟悉,但是多少还是见过的。就是那些类是BeginXXX和EndXXX的方法,且BeginXXX返回值是IAsyncResult接口。

在正式写APM示例之前我们先给出一段同步代码:

button1_Click( +</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> request = WebRequest.Create(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;<a href="https://m.jb51.cc/tag/http/" target="_blank" >http</a>s://github.com/</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;);<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//为了更好的演示<a href="https://www.jb51.cc/tag/xiaoguo/" target="_blank" >效果</a>,<a href="https://m.jb51.cc/tag/women/" target="_blank" >我们</a><a href="https://m.jb51.cc/tag/shiyong/" target="_blank" >使用</a>网速比较慢的外网</span>request.GetResponse();</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;发送请求 </span>

<span >
DeBUG.Writeline(<span >"<span >【DeBUG】线程ID:<span >" +<span > Thread.CurrentThread.ManagedThreadID);
label1.Text = <span >"<span >执行完毕!<span >"<span >;
}

【说明】为了更好的演示异步效果,这里我们使用winform程序来做示例。

【效果图】

看图得知:

我们在执行方法的时候页面出现了“假死”,拖不动了。我们看到打印结果,方法调用前和调用后线程ID都是9(也就是同一个线程)

下面我们再来演示对应的异步方法:(

button2_Click( DeBUG.Writeline( +</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> request = WebRequest.Create(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;<a href="https://m.jb51.cc/tag/http/" target="_blank" >http</a>s://github.com/</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;);request.BeginGetResponse(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span> AsyncCallback(t =><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//执行完成后的回调</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> response = request.EndGetResponse(t); <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> stream = response.GetResponseStream();<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" >获取</a>返回数据流 </span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;using</span> (StreamReader reader = <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; StreamReader(stream)) { StringBuilder sb </span>= <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; StringBuilder(); </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;while</span> (!<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;reader.EndOfStream) { </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> content =<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; reader.Read<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(); sb.Append(content); } De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;【De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>】</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span> + sb.ToString().Trim().Substring(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800080;"&gt;0</span>,<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800080;"&gt;100</span>) + <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;...</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span>);<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;只取返回<a href="https://www.jb51.cc/tag/neirong/" target="_blank" >内容</a>的前100个字符 </span> De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;【De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>】异步线程<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>:</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span> +<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Thread.CurrentThread.ManagedThread<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>); label1.Invoke((Action)(() </span>=> { label1.Text = <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;执行完毕!</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span>; }));<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;<a href="https://m.jb51.cc/tag/zheli/" target="_blank" >这里</a>跨线程访问UI需<a href="https://www.jb51.cc/tag/yaozuo/" target="_blank" >要做</a>处理</span>

<span > }
}),<span >null<span >);

De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;【De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>】主线程<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>:</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span> +<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Thread.CurrentThread.ManagedThread<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>); 

}

【效果图】

 

看图得知:

启用异步方法并没有是UI界面卡死异步方法启动了另外一个ID为12的线程

上面代码执行顺序:

前面我们说过,APM的BebinXXX必须返回IAsyncResult接口。那么接下来我们分析IAsyncResult接口:

首先我们看:

确实返回的是IAsyncResult接口。那IAsyncResult到底长的什么样子?:

并没有想象中的那么复杂嘛。我们是否可以尝试这实现这个接口,然后显示自己的异步方法呢?

首先定一个类MyWebRequest,然后继承IAsyncResult:

{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; WaitHandle AsyncWaitHandle{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;get</span> { <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;throw</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; NotImplementedException(); }}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;bo<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; CompletedSynchronously{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;get</span> { <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;throw</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; NotImplementedException(); }}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;bo<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; IsCompleted{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;get</span> { <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;throw</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; NotImplementedException(); }}

}

这样肯定是不能用的,起码也得有个存回调函数的属性吧,下面我们稍微改造下:

然后我们可以自定义APM异步模型了:

asyncResult = MyWebRequest(callback, request = WebRequest.Create( Thread(() => (StreamReader sr = str =}).Start();</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;return</span> asyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t;<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//返</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;回<a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>IAsyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t</span>

<span >}

<span >public <span >string<span > MyEndXX(IAsyncResult asyncResult)
{
MyWebRequest result = asyncResult <span >as<span > MyWebRequest;
<span >return<span > result.Result;
}

调用如下:

button4_Click( + AsyncCallback(t => result = + result.Trim().Substring(,) + + +

效果图:

我们看到自己实现的效果基本上和系统提供的差不多。

启用异步方法并没有是UI界面卡死异步方法启动了另外一个ID为11的线程

【总结】

个人觉得APM异步模式就是启用另外一个线程执行耗时任务,然后通过回调函数执行后续 *** 作。

APM还可以通过其他方式获取值,如:

(!{ Thread.Sleep( stream2 = request.EndGetResponse(asyncResult).GetResponseStream();

stream2 = request.EndGetResponse(asyncResult).GetResponseStream();

补充:如果是普通方法,我们也可以通过委托异步:

func = Func<,>(t => + t + </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;var</span> asyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t = func.BeginInvoke(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;张三</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span>,t =><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; { </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;string</span> str =<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; func.EndInvoke(t); De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(str); },</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;n<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>l</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;);

}

EAP

EAP 基于事件的异步模式,Event-based Asynchronous Pattern

此模式在C#2的时候随之而来。

先来看个EAP的例子:

button3_Click( + Backgroun<a href="https://m.jb51.cc/tag/DW/" target="_blank" >DW</a>orker worker </span>= <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Backgroun<a href="https://m.jb51.cc/tag/DW/" target="_blank" >DW</a>orker(); worker.DoWork </span>+= <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span> DoWorkEventHandler((s1,s2) =><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; { Thread.Sleep(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800080;"&gt;2000</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;); De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;【De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>】异步线程<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>:</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span> +<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Thread.CurrentThread.ManagedThread<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>); });</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;<a href="https://www.jb51.cc/tag/zhuce/" target="_blank" >注册</a>事件来实现异步</span> worker.RunWorkerAsync(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;this</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;); De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;【De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>】主线程<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>:</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span> +<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Thread.CurrentThread.ManagedThread<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>);

}

【效果图】

【特征】

通过事件的方式注册回调函数通过 XXXAsync方法来执行异步调用

例子很简单,但是和APM模式相比,是不是没有那么清晰透明。为什么可以这样实现?事件的注册是在干嘛?为什么执行RunWorkerAsync会触发注册的函数?

感觉自己又想多了...

我们试着反编译看看源码:

 只想说,这么玩,有意思吗?

TAP

TAP 基于任务的异步模式,Task-based Asynchronous Pattern

到目前为止,我们觉得上面的APM、EAP异步模式好用吗?好像没有发现什么问题。再仔细想想...如果我们有多个异步方法需要按先后顺序执行,并且需要得到所有返回值。

首先定义三个委托:

Func<,> Func<,>(t => + Func<,> Func<,>(t => + Func<,> Func<,>(t => +

然后按照一定顺序执行:

str1 = .Empty,str2 = .Empty,str3 = = ,asyncResult2 = ,asyncResult3 = = func1().BeginInvoke(,t =>= += func2().BeginInvoke(,a =>= += func3().BeginInvoke(,s =>= +asyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t1.AsyncWaitHandle.WaitOne();asyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t2.AsyncWaitHandle.WaitOne();asyncRes<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t3.AsyncWaitHandle.WaitOne();De<a href="https://m.jb51.cc/tag/BUG/" target="_blank" >BUG</a>.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(str1 </span>+ str2 +<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; str3);

}<span > 

除了难看、难读一点好像也没什么 。不过真的是这样吗?

asyncResult2是null?由此可见在完成第一个异步 *** 作之前没有对asyncResult2进行赋值,asyncResult2执行异步等待的时候报异常。那么如此我们就无法控制三个异步函数,按照一定顺序执行完成后再拿到返回值。

是的,现在该我们的TAP登场了。

只需要调用Task类的静态方法Run,即可轻轻松松使用异步。

获取返回值:

task1 = Task<>.Run(() => + value = task1.Result;Console.Writeline( + Thread.CurrentThread.ManagedThreadID);

现在我们处理上面多个异步按序执行:

Console.Writeline( + str1 = .Empty,str3 = task1 = Task.Run(() =>= +=>= +=>= +Thread.Sleep(<span >2500);<span >//<span >其他逻辑代码
<span >
task1.Wait();

DeBUG.Writeline(str1 + str2 +<span > str3);
Console.Writeline(<span >"<span >【DeBUG】主 线程ID:<span >" + Thread.CurrentThread.ManagedThreadID);

[效果图]

我们看到,结果都得到了,且是异步按序执行的。且代码的逻辑思路非常清晰。

延伸思考

WaitOne完成等待的原理

异步为什么会提升性能

线程的使用数量和cpu的使用率有必然的联系吗

问题1:WaitOne完成等待的原理

在此之前,我们先来简单的了解下多线程信号控制autoresetEvent类。

_asyncWaitHandle = autoresetEvent(

此代码会在  的地方会一直等待下去。除非有另外一个线程执行  的set方法。

_asyncWaitHandle = autoresetEvent(

如此,到了  就可以直接执行下去。没有有任何等待。

现在我们对APM 异步编程模型中的  等待是不是知道了点什么呢。我们回头来实现之前自定义异步方法的异步等待。

MyWebRequest(AsyncCallback asyncCallback,= } SetComplete(== (_asyncCallback != Result { ; {
    <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #ff0000;"&gt;get { return</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #ff0000;"&gt; _asyncWaitHandle; }</span>}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" >获取</a><a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>值,该值指示异步 *** 作是否<a href="https://m.jb51.cc/tag/tongbu/" target="_blank" >同步</a>完成。</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;bo<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; CompletedSynchronously{    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;get</span> { <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;throw</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; NotImplementedException(); }}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;//</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000;"&gt;<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" >获取</a><a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>值,该值指示异步 *** 作是否已完成。</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;bo<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; IsCompleted{    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;get</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;;    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;private</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;set</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;;}

}

红色代码就是新增的异步等待。

【执行步骤】

问题2:异步为什么会提升性能

比如同步代码:

Thread.Sleep();Thread.Sleep();

这个代码需要20秒。

如果是异步:

task = Task.Run(() =>););task.Wait();

如此就只要10秒了。这样就节约了10秒。

如果是:

task = Task.Run(() =>);

异步执行中间没有耗时的代码那么这样的异步将是没有意思的。

或者:

task = Task.Run(() =>););

把耗时任务放在异步等待后,那这样的代码也是不会有性能提升的。

还有一种情况:

如果是单核cpu进行高密集运算 *** 作,那么异步也是没有意义的。

问题3:线程的使用数量和cpu的使用率有必然的联系吗

答案是否。

还是拿单核做假设。

情况1:

num = (+= Random().Next(-,}

单核下,我们只启动一个线程,就可以让你cpu爆满。

启动八次,八进程cpu基本爆满。

情况2:

一千多个线程,而cpu的使用率竟然是0。由此,我们得到了之前的结论,线程的使用数量和cpu的使用率没有必然的联系。

虽然如此,但是也不能毫无节制的开启线程。因为:

开启一个新的线程的过程是比较耗资源的。多线程的切换也是需要时间的。每个线程占用了一定的内存保存线程上下文信息。

demo:

本文已同步至索引目录:《》

对于异步编程了解不深,文中极有可能多处错误描述和观点。

感谢广大园友的指正。

本着相互讨论的目的,绝无想要误导大家的意思。

【推荐】

href="http://www.cnblogs.com/wisdomqq/archive/2012/03/26/2412349.HTML" target="_blank">http://www.cnblogs.com/wisdomqq/archive/2012/03/26/2412349.HTML

总结

以上是内存溢出为你收集整理的C#异步的世界【上】全部内容,希望文章能够帮你解决C#异步的世界【上】所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1264198.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存