亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Flutter?異步編程之單線程下異步模型圖文示例詳解

 更新時間:2022年09月19日 08:54:51   作者:張風(fēng)捷特烈  
這篇文章主要為大家介紹了Flutter?異步編程之單線程下異步模型圖文示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

一、 本專欄圖示概念規(guī)范

本專欄是對 異步編程 的系統(tǒng)探索,會通過各個方面去認(rèn)知、思考 異步編程 的概念。期間會用到一些圖片進行表達與示意,在一開始先對圖中的元素和 基本概念 進行規(guī)范和說明。

1. 任務(wù)概念規(guī)范

任務(wù) : 完成一項需求的基本單位。

分發(fā)任務(wù): 觸發(fā)任務(wù)開始的動作。

任務(wù)結(jié)束: 任務(wù)完成的標(biāo)識。

任務(wù)生命期: 任務(wù)從開始到完成的時間跨度。

如下所示,方塊 表示任務(wù);當(dāng) 箭頭指向一個任務(wù)時,表示對該任務(wù)進行分發(fā);任何被分發(fā)的任務(wù)都會結(jié)束。在任務(wù)分發(fā)和結(jié)束之間,有一條虛線進行連接,表示 任務(wù)生命期 。

2. 任務(wù)的狀態(tài)

未完成 : Uncompleted
成功完成 : Completed with Success
異常結(jié)束 : Completed with Error

一個任務(wù)生命期間有三種狀態(tài),如下通過三種顏色表示。在 任務(wù)結(jié)束 之前,該任務(wù)都是 未完成 態(tài),通過 淺藍(lán)色 表示;任何被分發(fā)的任務(wù)都是為了完成某項需求,任何任務(wù)都會結(jié)束,在結(jié)束時刻根據(jù)是否完成需求,可分為 成功完成 和 異常結(jié)束 兩種狀態(tài),如下分別用 綠色 和 紅色 表示。

3. 時刻與時間線

機體 : 任務(wù)分發(fā)者或處理者。
時刻: 機體運行中的某一瞬間。
時間線: 所有時刻構(gòu)成的連續(xù)有向軸線。

在一個機體運行的過程中,時間線是絕對的,通過 紫色有向線段 表示時間的流逝的方向。時刻 是時間線上任意一點 ,通過 黑點 表示。

4.同步與異步

同步 : 機體在時間線上,將任務(wù)按順序依次分發(fā)。

同步執(zhí)行任務(wù)時,前一個任務(wù)完成后,才會分發(fā)下一任務(wù)。意思是說: 任意時刻只有一個任務(wù)在生命期中。

異步: 機體在時間線上,在一個任務(wù)未完成時,分發(fā)另一任務(wù)。

也就是說通過異步編程,允許某時刻有兩個及以上的任務(wù)在生命期中。如下所示,在 任務(wù)1 完成后,分發(fā) 任務(wù)2; 在 任務(wù)2 未結(jié)束的情況下,可以分發(fā) 任務(wù) 3 。此時對于任務(wù) 3 來說,任務(wù) 2 就是異步執(zhí)行的。

二、理解單線程中的異步任務(wù)

上面對基本概念進行了規(guī)范,看起來可能比較抽象,下面我們通過一個小場景來理解一下。媽媽早上出門散步,臨走前囑咐:

小捷,別睡了??炱鸫玻驯蛔訒褚幌?,地掃一下。還有,沒開水了,記得燒。

當(dāng)前場景下只有小捷 一個機體,需要完成的任務(wù)有四個:起床、曬被、拖地 、燒水 。

1. 任務(wù)的分配

當(dāng)機體有多個任務(wù)需要分發(fā)時,需要對任務(wù)進行分配。認(rèn)識任務(wù)之間的關(guān)系,是任務(wù)分配的第一步。只有理清關(guān)系,才能合理分配任務(wù)。分配過程中需要注意:

[1] 任務(wù)之間可能存在明確的先后順序,比如起床 需要在 曬被 之前。
[2] 任務(wù)之間先后順序也可能無所謂,比如先掃地還是先曬被,并沒有太大區(qū)別。
[3] 某類任務(wù)只需要機體來分發(fā),生命期中不需要機體處理,并且和后續(xù)的任務(wù)沒有什么關(guān)聯(lián)性,比如燒水任務(wù)。

像燒水這種任務(wù),即耗時,又不需要機體在任務(wù)生命期中做什么事。如果這類任務(wù)使用同步處理,那么任務(wù)期間機體能做的事只有 等待 。對于一個機體來說,這種等待就會意味著阻塞,不能處理任何事。

結(jié)合日常生活,我們知道當(dāng)前場景之中,想要發(fā)揮機體最大的效力,最好的方式是起床之后,先分發(fā) 燒水任務(wù),不需要等待燒水任務(wù)完成,就去執(zhí)行曬被、掃地任務(wù)。這樣的任務(wù)分配就是將 燒水 作為一個異步任務(wù)來執(zhí)行的。

但在如果在分配時,將燒水作為最后一個任務(wù),那么異步執(zhí)行的價值就會消失。所以對任務(wù)的合理分配,對機體的處理效率是非常重要的。

2.異步任務(wù)特點

從上面可以看出,異步任務(wù) 有很明顯的特征,并不是任何任務(wù)都有必要異步執(zhí)行。特別是對于單一機體來說,任務(wù)生命期間需要機體親自參與,是無法異步處理的。 比如一個人不能一邊曬被 ,一邊 掃地 。所以對于單線程來說,像一些只需要 分發(fā)任務(wù),任務(wù)的具體執(zhí)行邏輯由其他機體完成的任務(wù),適合使用 異步 處理,來避免不必要的等待。

這種任務(wù),在應(yīng)用程序中最常見的是網(wǎng)絡(luò) io和 磁盤 io 的任務(wù)。比如,從一個網(wǎng)絡(luò)接口中獲取數(shù)據(jù),對于機體來說,只需要分發(fā)任務(wù)來發(fā)送請求,就像燒水時只需要裝水按下啟動鍵一樣。而服務(wù)器如何根據(jù)請求,查詢數(shù)據(jù)庫來返回響應(yīng)信息,數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸?shù)?,和分發(fā)任務(wù)的機體沒有關(guān)系。磁盤的訪問也是一樣,分發(fā)讀寫文件任務(wù)后,真正干活的是操作系統(tǒng)。

像這類任務(wù)通過異步處理,可以避免在分發(fā)任務(wù)后,機體因等待任務(wù)的結(jié)束而阻塞。在等待其他機體處理的過程中,去分發(fā)其他任務(wù),可以更好地分配時間。比如下面所示,網(wǎng)絡(luò)數(shù)據(jù)獲取 的任務(wù)分發(fā)后,需要通過網(wǎng)絡(luò)把請求傳輸給服務(wù)器,服務(wù)器進行處理,給出響應(yīng)結(jié)果。

整個任務(wù)處理的過程,并不需要機體參與,所以分發(fā) 網(wǎng)絡(luò)數(shù)據(jù)獲取 任務(wù)后,無需等待任務(wù)完成,接著分發(fā) 構(gòu)建加載中界面 的任務(wù),來展示加載中的界面。從而給出用戶交互的反饋,而不是阻塞在那里等待網(wǎng)絡(luò)任務(wù)完成,這就是一個非常典型的異步任務(wù)使用場景。

3. 異步任務(wù)完成與回調(diào)

前面的介紹中可以看出,異步任務(wù)在分發(fā)之后,并不會等待任務(wù)完成,在任務(wù)生命期中,可以繼續(xù)分發(fā)其他任務(wù)。但任何任務(wù)都會結(jié)束,很多時候我們需要知道異步任務(wù)何時完成,以及任務(wù)的完成情況、任務(wù)返回的結(jié)果,以便該任務(wù)后續(xù)的處理。比如,在燒水完成之后,我們需要處理 沖水 的任務(wù)。

這就要涉及到一個對異步而言非常重要的概念:

回調(diào): 任務(wù)在生命期間向機體提供通知的方式。

比如 燒水 任務(wù)完成后,燒水壺 “叮” 的一聲通知任務(wù)完成;或者燒水期間發(fā)生故障,發(fā)出報警提示。這種在任務(wù)生命期間向機體發(fā)送通知的方式稱為回調(diào) 。在編程中,回調(diào)一般是通過 函數(shù)參數(shù) 來實現(xiàn)的,所以習(xí)慣稱 回調(diào)函數(shù) 。 另外,函數(shù)可以傳遞數(shù)據(jù),所以通過回調(diào)函數(shù)不僅可以知道任務(wù)結(jié)束的契機,還可以通過回調(diào)參數(shù)將任務(wù)的內(nèi)部數(shù)據(jù)暴露給機體。

比如在實際開發(fā)中,分發(fā) 網(wǎng)絡(luò)數(shù)據(jù)獲取 的任務(wù),其目的是為了通過網(wǎng)絡(luò)接口獲取數(shù)據(jù)。就像燒開水任務(wù)完成之后,需要把 開水 倒入瓶中一樣。我們也需要知道 網(wǎng)絡(luò)數(shù)據(jù)獲取 的任務(wù)完成的時機,將獲取的數(shù)據(jù) "倒入" 界面中進行顯示。

從發(fā)送異步任務(wù),到異步任務(wù)結(jié)束的回調(diào)觸發(fā),就是一個異步任務(wù)完整的 生命期。

三、 Dart 語言中的異步

上面只是介紹了 異步模型 中的概念,這些概念是共通的,無論什么編程語言都一樣適用。就像現(xiàn)實中,無論使用哪國的語言表述,四則運算的概念都不會有任何區(qū)別。只是在表述過程中,表現(xiàn)形式會在語言的語法上有所差異。

1.編程語言中與異步模型的對應(yīng)關(guān)系

每種語言的描述,都是對概念模型的具象化實現(xiàn)。這里既然是對 Flutter 中異步編程的介紹,自然要說一下 Dart 語言對異步模型的描述。

對于 任務(wù) 概念來說,在編程中和 函數(shù) 有著千絲萬縷的聯(lián)系:函數(shù)體 可以實現(xiàn) 任務(wù)處理的具體邏輯,也可以觸發(fā) 任務(wù)分發(fā)的動作 。但我并不認(rèn)為兩者是等價的, 任務(wù) 有著明確的 目的性 ,而 函數(shù) 是實現(xiàn)這種 目的 的手段。在編程活動中,函數(shù) 作為 任務(wù) 在代碼中的邏輯體現(xiàn),任務(wù) 應(yīng)先于 函數(shù) 存在。

如下代碼所示,在 main 函數(shù)中,觸發(fā) calculate 任務(wù),計算 0 ~ count 累加值和計算耗時,并返回。其中 calculate 函數(shù)就是對該任務(wù)的代碼實現(xiàn):

void main(){
  TaskResult result = calculate();
}
TaskResult calculate({int count = 10000000}){
  int startTime = DateTime.now().millisecondsSinceEpoch;
  int result = loopAdd(count);
  int cost = DateTime.now().millisecondsSinceEpoch-startTime;
  return TaskResult(
    cost:cost,
    data:result,
    taskName: "calculate"
  );
}
int loopAdd(int count) {
  int sum = 0;
  for (int i = 0; i <= count; i++) {
    sum+=i;
  }
  return sum;
}

這里 TaskResult 類用于記錄任務(wù)完成的信息:

class TaskResult {
  final int cost;
  final String taskName;
  final dynamic data;
  TaskResult({
    required this.cost,
    required this.data,
    required this.taskName,
  });
  Map<String,dynamic> toJson()=>{
    "taskName":taskName,
    "cost":cost,
    "data": data
  };
}

2.Dart 編程中的異步任務(wù)

如下在計算之后,還有兩個任務(wù):saveToFile 任務(wù),將運算結(jié)果保存到文件中;以及 render 任務(wù)將運算結(jié)果渲染到界面上。

void main() {
  TaskResult result = cacaulate();
  saveToFile(result);
  render(result);
}

這里 render 任務(wù)暫時通過在控制臺打印顯示作為渲染,邏輯如下:

void render(TaskResult result) {
  print("結(jié)果渲染: ${result.toJson()}");
}

下面是將結(jié)果寫入文件的任務(wù)實現(xiàn)邏輯。其中 File 對象的 writeAsString 是一個異步方法,可以將內(nèi)容寫入到文件中。通過 then 方法設(shè)置回調(diào),監(jiān)聽任務(wù)完成的時機。

void saveToFile(TaskResult result) {
  String filePath = path.join(Directory.current.path, "out.json");
  File file = File(filePath);
  String content = json.encode(result);
  file.writeAsString(content).then((File value){
    print("寫入文件成功:!${value.path}");
  });
}

3.當(dāng)前任務(wù)分析

如下是這三個任務(wù)的執(zhí)行示意,在 saveToFile 中使用 writeAsString 方法將異步處理寫入邏輯。

這樣就像在燒水任務(wù)分發(fā)后,可以執(zhí)行曬被一樣。saveToFile 任務(wù)分發(fā)之后,不需要等待文件寫入完成,可以繼續(xù)執(zhí)行 render 方法。日志輸出如下:渲染任務(wù)的執(zhí)行并不會因?qū)懭胛募蝿?wù)而阻塞,這就是異步處理的價值。

四、異步模型的延伸

1. 單線程異步模型的局限性

本文主要介紹 異步模型 的概念,認(rèn)識異步的作用,以及 Dart 編程語言中異步方法的基本使用。至于代碼中更具體的異步使用方式,將在后期文章中結(jié)合詳細(xì)介紹。另外,一般情況下,Dart 是以 單線程 運行的,所以本文中強調(diào)的是 單線程 下的異步模型。

仔細(xì)思考一下,可以看出,單線程中實現(xiàn)異步是有局限性的。比如說需要解析一個很大的 json ,或者進行復(fù)雜的邏輯運算等 耗時任務(wù),這種必須由 本機體 處理的邏輯,而不是 等待結(jié)果 的場景,是無法在單線程中異步處理的。

就像是 掃地 和 曬被 任務(wù),對于單一機體來說,不可能同時參與到兩個任務(wù)之中。在實際開發(fā)中這兩個任務(wù)可類比為 解析超大 json 和 顯示解析中界面 兩個任務(wù)。如果前者耗時三秒,由于單線程 中同步方法的阻塞,界面就會卡住三秒,這就是單線程異步模型的 局限性。

2. 多線程與異步的關(guān)系

上面問題的本質(zhì)矛盾是:一個機體無法 同時 參與到兩件任務(wù) 具體執(zhí)行過程中。解決方案也非常簡單,一個人搞不定,就搖人唄。多個機體參與任務(wù)分配的場景,就是 多線程 。 很多人都會討論 異步 和 多線程 的關(guān)系,其實很簡單:兩個機體,一個 掃地,一個 曬被,同一時刻,存在兩個及以上的任務(wù)在生命期中,一定是異步的。毫無疑問,多線程 是 異步模型 的一種實現(xiàn)方式。

3. Dart 中如何解決單線程異步模型的局限性

像 C++ 、Java 這些語言有 多線程 的支持,通過 “搖人” 可以充分調(diào)度 CPU 核心,來處理一些計算密集型的任務(wù),實現(xiàn)任務(wù)在時間上的最合理分配。

絕大多數(shù)人可能覺得 Dart 是一個單線程的編程語言,其實不然??赡苁呛芏嗳瞬]有在 Flutter 端做過計算密集型的任務(wù),沒有對多線程迫切的需要。畢竟 移動/桌面客戶端 大多是網(wǎng)絡(luò)、數(shù)據(jù)庫訪問等 io 密集型 的任務(wù),人手一個終端,沒有什么高并發(fā)的場景。不像后端那樣需要保證一個終端被百萬人同時訪問。

或者計算密集型的任務(wù)都有由平臺機體進行處理,將結(jié)果通知給 Flutter 端。這導(dǎo)致 Dart 看起來更像是一個 任務(wù)分發(fā)者,發(fā)號施令的人,絕大多數(shù)時候并不需要親自參與任務(wù)的執(zhí)行過程中。而這正是單線程下的異步模型所擅長的:借他人之力,監(jiān)聽回調(diào)信息。

其實我們在日常開發(fā)中,使用的平臺相關(guān)的插件,其中的方法基本上都是異步的,本質(zhì)上就是這個原因。平臺 是個燒水壺,燒水任務(wù)只需要分發(fā) 和 監(jiān)聽回調(diào)。至于水怎么燒開,是 平臺 需要關(guān)心的,這和 網(wǎng)絡(luò) io 、磁盤 io 是很類似的,都是 請求 與 響應(yīng) 的模式。這種任務(wù),由單線程的異步模型進行處理,是最有效的,畢竟 “搖人” 還是要管飯的。

那如果非要在 Dart 中處理計算密集型的任務(wù),該如何是好呢?不用擔(dān)心,Dart 的 isolate 機制可以完成這項需求。關(guān)于這點,在后面會進行詳述。認(rèn)識 異步 是什么,是本文的核心,那本文就到這里,謝謝觀看 ~

更多關(guān)于Flutter 單線程異步模型的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論