日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Dart多個future隊列完成加入順序關系及原子性論證_Dart

作者:李小轟_Rex ? 更新時間: 2022-12-11 編程語言

引言

Dart 是一個在單線程中運行的程序。在程序中執行一個需要長時間的執行的操作,為避免卡住UI主線程,我們會用到異步(future),可以使程序在等待一個耗時操作完成時繼續處理其他工作。

在進入正題之前,我們先了解一下 Dart 的消息循環機制:

  • Dart 從兩個隊列執行任務:Event事件隊列Microtask微任務隊列
  • 事件循環會優先處理微任務隊列,microtask清空之后才將 event事件隊列中的下一個項目出隊并處理
  • 事件隊列具有來自Dart(Future,Timer,Isolate Message等)和系統(用戶輸入,I/O等)
  • 微任務隊列目前僅包含來自Dart,當然我們也可以自己往微隊列中插入任務

什么是 Future

Future 是什么?這里我們用小篇幅簡單描述一下。future是一個異步的執行操作,可以在不阻塞代碼的情況下實現耗時功能。

main() {
  Future(() {
    print('我是一個耗時操作');
  }).then((value){
    print('future 結束了');
  });
  print('main');
}

?//打印結果
main
我是一個耗時操作
future 結束了

在項目中,我們使用 FutureAsyncawait 相互組合實現異步編碼。

Future 操作具備'原子性'嗎

上面的篇幅我們說過,future 創建后會被直接加入到事件隊列依次執行。那么在上一個 future 在沒有標識完成前,下一個 future 可以被執行嗎?

小編寫了幾個樣例來做實驗:

實驗寫法一:

main() {
  Test1.future1().then((value) => print(value));
  Test1.future2().then((value) => print(value));
}
abstract class Test1 {
  static Future<String> future1() async {
    return Future(() async {
      print('開始 future1');
      await TestTool.timeConsume(1000000000); //耗時運算
      print('一千年過去了');
      return 'future1 結束了';
    });
  }
  static Future<String> future2() async {
    return Future(() async {
      print('開始 future2');
      await TestTool.timeConsume(1000); //耗時運算
      print('繼續 future2');
      return 'future2 結束了';
    });
  }
}

?//打印結果
開始 future1
一千年過去了
future1 結束了
開始 future2
繼續 future2
future2 結束了

實驗結果:

  • 從打印結果上看,future任務沒有中斷,執行完當前任務后才可執行隊列中的下一個future

實驗寫法二:

main() {
  Test2.future1().then((value) => print(value));
  Test2.future2().then((value) => print(value));
}
abstract class Test2 {
  static Future<String> future1() async {
    print('開始 future1');
    await TestTool.timeConsume(1000000000);//耗時運算
    print('一千年過去了');
    return 'future1 結束了';
  }
  static Future<String> future2() async {
    print('開始 future2');
    await TestTool.timeConsume(1000);//耗時運算
    print('繼續 future2');
    return 'future2 結束了';
  }
}

//打印結果
開始 future1
開始 future2
一千年過去了
future1 結束了
繼續 future2
future2 結束了

實驗結果:

  • future2future1沒有結束前就已經開始了任務。
  • future2會在future1任務執行完成后響應結束,整個過程仍然保持了完成順序與加入事件隊列的順序一致性。

實驗寫法三:

main() {
  Test3.future1().then((value) => print(value));
  Test3.future2().then((value) => print(value));
}
abstract class Test3 {
  static Future<String> future1() async {
    print('開始 future1');
    await Future(() => TestTool.timeConsume(1000000000));//耗時運算
    print('一千年過去了');
    return 'future1 結束了';
  }
  static Future<String> future2() async {
    print('開始 future2');
    await TestTool.timeConsume(1000);//耗時運算
    print('繼續 future2');
    return 'future2 結束了';
  }
}

//打印結果
開始 future1
開始 future2
繼續 future2
future2 結束了
一千年過去了
future1 結束了

實驗結果:

  • 從打印結果上看,future1開始后,future2直接開始任務,且future2任務完成后直接標識完成。
  • future1future2 的完成順序已經和加入事件隊列的順序無關了,只與內部耗時正相關。

附上耗時代碼:

abstract class TestTool {
  ///耗時操作
  static Future<int> timeConsume(int num) async {
    final result = _timeConsume(num);
    return result;
  }
  static int _timeConsume(int num) {
    int count = 0;
    while (num > 0) {
      if (num % 2 == 0) {
        count++;
      }
      num--;
    }
    return count;
  }
}

論證結論

綜合上述三種寫法分析:

future方法體內部不屬于可靠的'原子性操作',不同的寫法有不同的差異性。 如果想將整個方法體內部作為不可拆分的執行單位。在外層使用Future進行包裹處理,如寫法一中Test1示例:

static Future<T> funcName() async {
  return Future(() async {
    ...
    具體的方法體內容
    ...
    return result;
  });
}

future在創建的同時,就會被加入到event事件隊列中。事件隊列是依次執行的,但每個future的完成順序與加入的順序不存在可靠的一致性。 如果在業務內想保持順序的一致性,可參考上述寫法,或使用 await 進行強制等待如:

main() async {
  await Test2.future1().then((value) => print(value));
  Test2.future2().then((value) => print(value));
}

這樣寫,future2 就一定會在 future1 執行完成后才進入開始狀態。

原文鏈接:https://juejin.cn/post/7164289959359152159

欄目分類
最近更新