flutter-async和await原理解析

一 . async await 與 Future

在異步調(diào)用中有三個關(guān)鍵詞,async、await、Future,其中async和await需要一起使用。在Dart中可以通過async和await進行異步操作,async表示開啟一個異步操作,也可以返回一個Future結(jié)果。如果沒有返回值,則默認返回一個返回值為null的Future。

await的操作,不會影響方法外后續(xù)代碼的執(zhí)行;只會阻塞async方法的后續(xù)代碼

例子1
_testAsyncKeyword() {
    print("test函數(shù)開始了:${DateTime.now()}");
    _testString().then((value) => print(value));
    print("test函數(shù)結(jié)束了:${DateTime.now()}");
  }

  Future<String> _testString() async {
    Future f = Future.delayed(Duration(milliseconds: 300), () {
      return "我是測試字符串===1";
    });
    String result = await f;
    print("我是測試字符串===2");
    return result;
  }
// flutter: test函數(shù)開始了:2021-05-27 13:58:56.595964
// flutter: test函數(shù)結(jié)束了:2021-05-27 13:58:56.598801
// flutter: 我是測試字符串===2
// flutter: 我是測試字符串===1

在代碼示例中,執(zhí)行到_testString()方法,會同步進入方法內(nèi)部進行執(zhí)行,當(dāng)執(zhí)行到await時就會停止async內(nèi)部的執(zhí)行,從而繼續(xù)執(zhí)行外面的代碼。所以await的操作,不會影響后面代碼的執(zhí)行("test函數(shù)結(jié)束了"會先于_testString()內(nèi)部打印)。
當(dāng)await有返回后,會繼續(xù)從await的位置繼續(xù)執(zhí)行(所以先打印出了 "我是測試字符串===2" ,然后返回Future的結(jié)果,并通過print打印出 "我是測試字符串===1")。

例子2
_testAsyncKeyword() async {
    print("test函數(shù)開始了:${DateTime.now()}");
    print(await _testString());
    print("test函數(shù)結(jié)束了:${DateTime.now()}");
  }

  Future<String> _testString() async {
    Future f = Future.delayed(Duration(milliseconds: 300), () {
      return "我是測試字符串===1";
    });
    String result = await f;
    print("我是測試字符串===2");
    return result;
  }

// flutter: test函數(shù)開始了:2021-05-27 14:06:48.185704
// flutter: 我是測試字符串===2
// flutter: 我是測試字符串===1
// flutter: test函數(shù)結(jié)束了:2021-05-27 14:06:48.497481

在代碼示例中, _testAsyncKeyword() 本身內(nèi)部就有一個await操作,當(dāng)執(zhí)行到await時就會停止_testAsyncKeyword() async內(nèi)部的執(zhí)行.等待_testString()有結(jié)果返回之后,繼續(xù)執(zhí)行.

_testString()內(nèi)部也是有一個await操作,當(dāng)執(zhí)行到await時就會停止_testString() async內(nèi)部的執(zhí)行,等待300毫秒,Future有結(jié)果后,打印字符串2

_testAsyncKeyword() 繼續(xù)執(zhí)行 打印 字符串1 及 結(jié)束

例子3
_testAsyncKeyword() {
    print("test函數(shù)開始了:${DateTime.now()}");
    firstString().then((value) => print(value));
    secondString().then((value) => print(value));
    thirdString().then((value) => print(value));
    print("test函數(shù)結(jié)束了:${DateTime.now()}");
  }

_testKeyword2() async{
    print("test函數(shù)開始了:${DateTime.now()}");
    print(await firstString());
    print(await secondString());
    print(await thirdString());
    print("test函數(shù)結(jié)束了:${DateTime.now()}");
  }
Future<String> firstString() {
    return Future.delayed(Duration(milliseconds: 300), () {
      return "我是一個字符串";
    });
  }

  Future<String> secondString() {
    return Future.delayed(Duration(milliseconds: 200), () {
      return "我是二個字符串";
    });
  }

  Future<String> thirdString() {
    return Future.delayed(Duration(milliseconds: 100), () {
      return "我是三個字符串";
    });
  }

//_testAsyncKeyword() 的打印:
//flutter: test函數(shù)開始了:2021-05-27 14:17:42.620409
//flutter: test函數(shù)結(jié)束了:2021-05-27 14:17:42.624359
//flutter: 我是三個字符串
//flutter: 我是二個字符串
//flutter: 我是一個字符串

//_testKeyword2() 的打印:
//flutter: test函數(shù)開始了:2021-05-27 14:18:41.401992
//flutter: 我是一個字符串
//flutter: 我是二個字符串
//flutter: 我是三個字符串
//flutter: test函數(shù)結(jié)束了:2021-05-27 14:18:42.027575

通過上面三個例子 , 大家應(yīng)該可以看出 await async 和 then之間的區(qū)別和聯(lián)系了吧.

二. async、await的原理

async、await的操作屬于"假異步",這是為什么呢?
如果想要得到這個問題的答案,首先我們需要了解async、await的原理,了解協(xié)程的概念,因為async、await本質(zhì)上就是協(xié)程的一種語法糖。協(xié)程,也叫作coroutine,是一種比線程更小的單元。如果從單元大小來說,基本可以理解為 進程->線程->協(xié)程。

1.任務(wù)調(diào)度

在弄懂協(xié)程之前,首先要明白并發(fā)和并行的概念

  • 并發(fā): 指的是由系統(tǒng)來管理多個IO的切換,并交由CPU去處理。
  • 并行: 指的是多核CPU在同一時間里執(zhí)行多個任務(wù)。

并發(fā)的實現(xiàn)由非阻塞操作+事件通知來完成,事件通知也叫做“中斷”。操作過程分為兩種,一種是CPU對IO進行操作,在操作完成后發(fā)起中斷告訴IO操作完成。另一種是IO發(fā)起中斷,告訴CPU可以進行操作。

線程: 本質(zhì)上也是依賴于中斷來進行調(diào)度的,線程還有一種叫做“阻塞式中斷”,就是在執(zhí)行IO操作時將線程阻塞,等待執(zhí)行完成后再繼續(xù)執(zhí)行,通過單線程并發(fā)可以進行大量并發(fā)操作。但線程的消耗是很大的,并不適合大量并發(fā)操作的處理,且單個線程只能使用到單個CPU。當(dāng)多核CPU出現(xiàn)后,單個線程就無法很好的利用多核CPU的優(yōu)勢了,所以又引入了線程池的概念,通過線程池來管理大量線程。當(dāng)需要同時執(zhí)行多項任務(wù)的時候,我們就會采用多線程并發(fā)執(zhí)行.

單線程并發(fā) http://www.itdecent.cn/p/65e7f173024e

Dart單線程運行模型: 輸入單吸納成運行機制,主要是通過消息循環(huán)機制來實現(xiàn)任務(wù)調(diào)度和處理的.

2.協(xié)程coroutine

關(guān)于協(xié)程 https://zhuanlan.zhihu.com/p/172471249
多線程并發(fā) 操作系統(tǒng)在線程等待IO的時候,會阻塞當(dāng)前線程,切換到其它線程,這樣在當(dāng)前線程等待IO的過程中,其它線程可以繼續(xù)執(zhí)行。當(dāng)系統(tǒng)線程較少的時候沒有什么問題,但是當(dāng)線程數(shù)量非常多的時候,卻產(chǎn)生了問題。一是系統(tǒng)線程會占用非常多的內(nèi)存空間,二是過多的線程切換會占用大量的系統(tǒng)時間。
協(xié)程 運行在線程之上,當(dāng)一個協(xié)程執(zhí)行完成后,可以選擇主動讓出,讓另一個協(xié)程運行在當(dāng)前線程之上。協(xié)程并沒有增加線程數(shù)量,只是在線程的基礎(chǔ)之上通過分時復(fù)用的方式運行多個協(xié)程,而且協(xié)程的切換在用戶態(tài)完成,切換的代價比線程從用戶態(tài)到內(nèi)核態(tài)的代價小很多。

協(xié)程分為無線協(xié)程和有線協(xié)程.

  • 無線協(xié)程在離開當(dāng)前調(diào)用位置時,會將當(dāng)前變量放在 堆區(qū),當(dāng)再次回到當(dāng)前位置時,還會繼續(xù)從堆區(qū)中獲取到變量。所以,一般在執(zhí)行當(dāng)前函數(shù)時就會將變量直接分配到堆區(qū),而async、await就屬于無線協(xié)程的一種。
  • 有線協(xié)程則會將變量繼續(xù)保存在 棧區(qū),在回到指針指向的離開位置時,會繼續(xù)從棧中取出調(diào)用。

3.async、await原理

之所以說async/await是假異步,是因為他在執(zhí)行過程中并沒有開啟新的線程更沒有并發(fā)執(zhí)行,而是通過單線程上的任務(wù)調(diào)度(協(xié)程,沒有并發(fā)執(zhí)行功能)實現(xiàn)的:
當(dāng)代碼執(zhí)行到async則表示進入一個協(xié)程,會同步執(zhí)行async的代碼塊。async的代碼塊本質(zhì)上也相當(dāng)于一個函數(shù),并且有自己的上下文環(huán)境。當(dāng)執(zhí)行到await時,則表示有任務(wù)需要等待,CPU則去調(diào)度執(zhí)行其他IO,也就是后面的代碼或其他協(xié)程代碼。過一段時間CPU就會輪詢一次,看某個協(xié)程是否任務(wù)已經(jīng)處理完成,有返回結(jié)果可以被繼續(xù)執(zhí)行,如果可以被繼續(xù)執(zhí)行的話,則會沿著上次離開時指針指向的位置繼續(xù)執(zhí)行,也就是await標(biāo)志的位置。

由于并沒有開啟新的線程,只是進行IO中斷改變CPU調(diào)度,所以網(wǎng)絡(luò)請求這樣的異步操作可以使用async、await,但如果是執(zhí)行大量耗時同步操作的話,應(yīng)該使用isolate開辟新的線程去執(zhí)行。

http://www.itdecent.cn/p/54da18ed1a9e

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請注明:劉小壯[http://www.itdecent.cn/u/2de707c93d...
    劉小壯閱讀 30,819評論 12 88
  • Flutter默認是單線程任務(wù)處理的,如果不開啟新的線程,任務(wù)默認在主線程中處理。 事件隊列 和iOS應(yīng)用很像,在...
    羈擁_f357閱讀 649評論 0 1
  • 異步IO CPU的速度遠遠快于磁盤、網(wǎng)絡(luò)等IO。在一個線程中,CPU執(zhí)行代碼的速度極快,然而,一旦遇到IO操作,如...
    時間之友閱讀 6,664評論 4 17
  • 一、async/await的優(yōu)點 1)方便級聯(lián)調(diào)用:即調(diào)用依次發(fā)生的場景; 2)同步代碼編寫方式: Promise...
    puxiaotaoc閱讀 105,904評論 7 62
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,927評論 0 4

友情鏈接更多精彩內(nèi)容