日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

探索c#之Async、Await剖析

 深秋微涼3 2016-02-07

(點擊上方藍字,可快速關注我們)


來自:蘑菇先生

鏈接:http://www.cnblogs.com/mushroom/p/4575417.html


基本介紹


Async、Await是net4.x新增的異步編程方式,其目的是為了簡化異步程序編寫,和之前APM方式簡單對比如下。


APM方式,BeginGetRequestStream需要傳入回調(diào)函數(shù),線程碰到BeginXXX時會以非阻塞形式繼續(xù)執(zhí)行下面邏輯,完成后回調(diào)先前傳入的函數(shù)。


HttpWebRequest myReq =(HttpWebRequest)WebRequest.Create('http://cnblogs.com/');

myReq.BeginGetRequestStream();

//to do


Async方式,使用Async標記Async1為異步方法,用Await標記GetRequestStreamAsync表示方法內(nèi)需要耗時的操作。主線程碰到await時會立即返回,繼續(xù)以非阻塞形式執(zhí)行主線程下面的邏輯。當await耗時操作完成時,繼續(xù)執(zhí)行Async1下面的邏輯


static async void Async1()

{

HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create('http://cnblogs.com/');

await myReq.GetRequestStreamAsync();

//to do

}


上面是net類庫實現(xiàn)的異步,如果要實現(xiàn)自己方法異步。


APM方式:


public delegate int MyDelegate(int x);

MyDelegate mathDel = new MyDelegate((a) => { return 1; });

mathDel.BeginInvoke(1, (a) => { },null);


Async方式:



static async void Async2()

{

await Task.Run(() => { Thread.Sleep(500); Console.WriteLine('bbb'); });

Console.WriteLine('ccc');

}

Async2();

Console.WriteLine('aaa');


對比下來發(fā)現(xiàn),async/await是非常簡潔優(yōu)美的,需要寫的代碼量更少,更符合人們編寫習慣。


因為人的思維對線性步驟比較好理解的。


APM異步回調(diào)的執(zhí)行步驟是:A邏輯->假C回調(diào)邏輯->B邏輯->真C回調(diào)邏輯,這會在一定程度造成思維的混亂,當一個項目中出現(xiàn)大量的異步回調(diào)時,就會變的難以維護。


Async、Await的加入讓原先這種混亂的步驟,重新?lián)苷?,?zhí)行步驟是:A邏輯->B邏輯->C邏輯。


基本原理剖析


作為一個程序員的自我修養(yǎng),刨根問底的好奇心是非常重要的。 Async剛出來時會讓人有一頭霧水的感覺,await怎么就直接返回了,微軟怎么又出一套新的異步模型。那是因為習慣了之前的APM非線性方式導致的,現(xiàn)在重歸線性步驟反而不好理解。 學習Async時候,可以利用已有的APM方式去理解,以下代碼純屬虛構。


比如把Async2方法想象APM方式的Async3方法:


static async void Async3()

{

var task= await Task.Run(() => { Thread.Sleep(500); Console.WriteLine('bbb'); });

//注冊task完成后回調(diào)

task.RegisterCompletedCallBack(() =>

{

Console.WriteLine('ccc');

});

}


上面看其來就比較好理解些的,再把Async3方法想象Async4方法:


static void Async4()

{

var thread = new Thread(() =>

{

Thread.Sleep(500);

Console.WriteLine('bbb');

});

//注冊thread完成后回調(diào)

thread.RegisterCompletedCallBack(() =>

{

Console.WriteLine('ccc');

});

thread.Start();

}


這樣看起來就非常簡單明了,連async都去掉了,變成之前熟悉的編程習慣。雖然代碼純屬虛構,但基本思想是相通的,差別在于實現(xiàn)細節(jié)上面。


內(nèi)部實現(xiàn)剖析


作為一個程序員的自我修養(yǎng),嚴謹更是不可少的態(tài)度。上面的基本思想雖然好理解了,但具體細節(jié)呢,編程是個來不得半點虛假的工作,那虛構的代碼完全對不住看官們啊。


繼續(xù)看Async2方法,反編譯后的完整代碼如下:


internal class Program

{

// Methods

[AsyncStateMachine(typeof(d__2)), DebuggerStepThrough]

private static void Async2()

{

d__2 d__;

d__.<>t__builder = AsyncVoidMethodBuilder.Create();

d__.<>1__state = -1;

d__.<>t__builder.Startd__2>(ref d__);

}


private static void Main(string[] args)

{

Async2();

Console.WriteLine('aaa');

Console.ReadLine();

}


// Nested Types

[CompilerGenerated]

private struct d__2 : IAsyncStateMachine

{

// Fields

public int <>1__state;

public AsyncVoidMethodBuilder <>t__builder;

private object <>t__stack;

private TaskAwaiter <>u__$awaiter3;


// Methods

private void MoveNext()

{

try

{

TaskAwaiter awaiter;

bool flag = true;

switch (this.<>1__state)

{

case -3:

goto Label_00C5;


case 0:

break;


default:

if (Program.CS$<>9__CachedAnonymousMethodDelegate1 == null)

{

Program.CS$<>9__CachedAnonymousMethodDelegate1 = new Action(Program.b__0);

}

awaiter = Task.Run(Program.CS$<>9__CachedAnonymousMethodDelegate1).GetAwaiter();

if (awaiter.IsCompleted)

{

goto Label_0090;

}

this.<>1__state = 0;

this.<>u__$awaiter3 = awaiter;

this.<>t__builder.AwaitUnsafeOnCompletedd__2>(ref awaiter, ref this);

flag = false;

return;

}

awaiter = this.<>u__$awaiter3;

this.<>u__$awaiter3 = new TaskAwaiter();

this.<>1__state = -1;

Label_0090:

awaiter.GetResult();

awaiter = new TaskAwaiter();

Console.WriteLine('ccc');

}

catch (Exception exception)

{

this.<>1__state = -2;

this.<>t__builder.SetException(exception);

return;

}

Label_00C5:

this.<>1__state = -2;

this.<>t__builder.SetResult();

}


[DebuggerHidden]

private void SetStateMachine(IAsyncStateMachine param0)

{

this.<>t__builder.SetStateMachine(param0);

}

}


public delegate int MyDelegate(int x);

}


Collapse Methods

發(fā)現(xiàn)async、await不見了,原來又是編譯器級別提供的語法糖優(yōu)化,所以說async不算是全新的異步模型。 可以理解為async更多的是線性執(zhí)行步驟的一種回歸,專門用來簡化異步代碼編寫。


從反編譯后的代碼看出編譯器新生成一個繼承IAsyncStateMachine 的狀態(tài)機結構asyncd(代碼中叫d__2,后面簡寫AsyncD),下面是基于反編譯后的代碼來分析的。


IAsyncStateMachine最基本的狀態(tài)機接口定義:


public interface IAsyncStateMachine

{

void MoveNext();

void SetStateMachine(IAsyncStateMachine stateMachine);

}


既然沒有了async、await語法糖的阻礙,就可以把代碼執(zhí)行流程按線性順序來理解,其整個執(zhí)行步驟如下:


1. 主線程調(diào)用Async2()方法

2. Async2()方法內(nèi)初始化狀態(tài)機狀態(tài)為-1,啟動AsyncD

3. MoveNext方法內(nèi)部開始執(zhí)行,其task.run函數(shù)是把任務扔到線程池里,返回個可等待的任務句柄。MoveNext源碼剖析:


//要執(zhí)行任務的委托


Program.CS$<>9__CachedAnonymousMethodDelegate1 = new Action(Program.b__0);


//開始使用task做異步,是net4.0基于任務task的編程方式。


awaiter =Task.Run(Program.CS$<>9__CachedAnonymousMethodDelegate1).GetAwaiter();


//設置狀態(tài)為0,以便再次MoveNext直接break,執(zhí)行switch后面的邏輯,典型的狀態(tài)機模式。


this.<>1__state = 0;


//返回調(diào)用async2方法的線程,讓其繼續(xù)執(zhí)行主線程后面的邏輯


this.<>t__builder.AwaitUnsafeOnCompletedd__2>(ref awaiter, ref this);

return;


4. 這時就已經(jīng)有2個線程在跑了,分別是主線程和Task.Run在跑的任務線程。


5. 執(zhí)行主線程后面邏輯輸出aaa,任務線程運行完成后輸出bbb、在繼續(xù)執(zhí)行任務線程后面的業(yè)務邏輯輸出ccc。


Label_0090:

awaiter.GetResult();

awaiter = new TaskAwaiter();

Console.WriteLine('ccc');


這里可以理解為async把整個主線程同步邏輯,分拆成二塊。 第一塊是在主線程直接執(zhí)行,第二塊是在任務線程完成后執(zhí)行, 二塊中間是任務線程在跑,其源碼中awaiter.GetResult()就是在等待任務線程完成后去執(zhí)行第二塊。


從使用者角度來看執(zhí)行步驟即為: 主線程A邏輯->異步任務線程B邏輯->主線程C邏輯。


Test();

Console.WriteLine('A邏輯');

static async void Test()

{

await Task.Run(() => { Thread.Sleep(1000); Console.WriteLine('B邏輯'); });

Console.WriteLine('C邏輯');

}


回過頭來對比下基本原理剖析小節(jié)中的虛構方法Async4(),發(fā)現(xiàn)區(qū)別在于一個是完成后回調(diào),一個是等待完成后再執(zhí)行,這也是實現(xiàn)異步最基本的兩大類方式。


重點注意的地方


主線程A邏輯->異步任務線程B邏輯->主線程C邏輯。


注意:這3個步驟是有可能會使用同一個線程的,也可能會使用2個,甚至3個線程。 可以用Thread.CurrentThread.ManagedThreadId測試下得知。


Async7();

Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

static async void Async7()

{

await Task.Run(() =>

{

Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

});

Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

}


正由于此,才會有言論說Async不用開線程,也有說需要開線程的,從單一方面來講都是對的,也都是錯的。 上面源碼是從簡分析的,具體async內(nèi)部會涉及到線程上下文切換,線程復用、調(diào)度等。 想深入的同學可以研究下ExecutionContextSwitcher、 SecurityContext.RestoreCurrentWI、ExecutionContext這幾個東東。


其實具體的物理線程細節(jié)可以不用太關心,知道其【主線程A邏輯->異步任務線程B邏輯->主線程C邏輯】這個基本原理即可。 另外Async也會有線程開銷的,所以要合理分業(yè)務場景去使用。


總結


從逐漸剖析Async中發(fā)現(xiàn),Net提供的異步方式基本上一脈相承的,如:


1. net4.5的Async,拋去語法糖就是Net4.0的Task+狀態(tài)機。

2. net4.0的Task, 退化到3.5即是(Thread、ThreadPool)+實現(xiàn)的等待、取消等API操作。


本文以async為起點,簡單剖析了其內(nèi)部原理及實現(xiàn),希望對大家有所幫助。



DotNet

微信號:iDotNet

打造東半球最好的 .Net 微信號

--------------------------------------

商務合作QQ:2302462408

投稿網(wǎng)址:top.jobbole.com

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多