Dart多個future隊列完成加入順序關系及原子性論證
引言
Dart 是一個在單線程中運行的程序。在程序中執(zhí)行一個需要長時間的執(zhí)行的操作,為避免卡住UI主線程,我們會用到異步(future),可以使程序在等待一個耗時操作完成時繼續(xù)處理其他工作。
在進入正題之前,我們先了解一下 Dart 的消息循環(huán)機制:
- Dart 從兩個隊列執(zhí)行任務:Event事件隊列 和 Microtask微任務隊列
- 事件循環(huán)會優(yōu)先處理微任務隊列,microtask清空之后才將 event事件隊列中的下一個項目出隊并處理
- 事件隊列具有來自Dart(Future,Timer,Isolate Message等)和系統(tǒng)(用戶輸入,I/O等)
- 微任務隊列目前僅包含來自Dart,當然我們也可以自己往微隊列中插入任務
什么是 Future
Future 是什么?這里我們用小篇幅簡單描述一下。future是一個異步的執(zhí)行操作,可以在不阻塞代碼的情況下實現耗時功能。
main() {
Future(() {
print('我是一個耗時操作');
}).then((value){
print('future 結束了');
});
print('main');
}
//打印結果
main
我是一個耗時操作
future 結束了
在項目中,我們使用 Future 、Async、await 相互組合實現異步編碼。
Future 操作具備'原子性'嗎
上面的篇幅我們說過,future 創(chuàng)建后會被直接加入到事件隊列依次執(zhí)行。那么在上一個 future 在沒有標識完成前,下一個 future 可以被執(zhí)行嗎?
小編寫了幾個樣例來做實驗:
實驗寫法一:
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('繼續(xù) future2');
return 'future2 結束了';
});
}
}
//打印結果
開始 future1
一千年過去了
future1 結束了
開始 future2
繼續(xù) future2
future2 結束了
實驗結果:
- 從打印結果上看,future任務沒有中斷,執(zhí)行完當前任務后才可執(zhí)行隊列中的下一個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('繼續(xù) future2');
return 'future2 結束了';
}
}
//打印結果
開始 future1
開始 future2
一千年過去了
future1 結束了
繼續(xù) future2
future2 結束了
實驗結果:
- future2在future1沒有結束前就已經開始了任務。
- future2會在future1任務執(zhí)行完成后響應結束,整個過程仍然保持了完成順序與加入事件隊列的順序一致性。
實驗寫法三:
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('繼續(xù) future2');
return 'future2 結束了';
}
}
//打印結果
開始 future1
開始 future2
繼續(xù) future2
future2 結束了
一千年過去了
future1 結束了
實驗結果:
- 從打印結果上看,future1開始后,future2直接開始任務,且future2任務完成后直接標識完成。
- future1 和 future2 的完成順序已經和加入事件隊列的順序無關了,只與內部耗時正相關。
附上耗時代碼:
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方法體內部不屬于可靠的'原子性操作',不同的寫法有不同的差異性。 如果想將整個方法體內部作為不可拆分的執(zhí)行單位。在外層使用Future進行包裹處理,如寫法一中Test1示例:
static Future<T> funcName() async {
return Future(() async {
...
具體的方法體內容
...
return result;
});
}
future在創(chuàng)建的同時,就會被加入到event事件隊列中。事件隊列是依次執(zhí)行的,但每個future的完成順序與加入的順序不存在可靠的一致性。 如果在業(yè)務內想保持順序的一致性,可參考上述寫法,或使用 await 進行強制等待如:
main() async {
await Test2.future1().then((value) => print(value));
Test2.future2().then((value) => print(value));
}
這樣寫,future2 就一定會在 future1 執(zhí)行完成后才進入開始狀態(tài)。
以上就是Dart多個future隊列完成加入順序關系及原子性論證的詳細內容,更多關于Dart多個future原子性的資料請關注腳本之家其它相關文章!

