在dart中不存在線程的概念嚼隘,也就沒有鎖類似的概念烂斋。如果有耗時(shí)操作辽俗,就需要引入Isolate機(jī)制
1 Isolate機(jī)制
void main() {
// Isolate有一個(gè)靜態(tài)方法。傳入的第一個(gè)參數(shù)是一個(gè)方法拴魄,這邊使用的匿名方法冗茸。第二個(gè)參數(shù)就是傳入方法的參數(shù)
Isolate.spawn((message) {
// 這里執(zhí)行的代碼都是子isolate中
sleep(const Duration(seconds: 10));
}, "我說從isolate傳遞到子isolate的數(shù)據(jù)");
}
那Isolate之間是怎么交互的呢?主isolate和子isolate的資源不共享
void main() {
// 創(chuàng)建一個(gè)消息接收器
var mainReceivePort = ReceivePort();
// 從消息接收器中讀取消息
mainReceivePort.listen((message) {
// 這里接收從子isolate發(fā)來的消息
print("主isolate接收器接收到消息:$message");
// 如果消息類型是SendPort羹铅,則說明是發(fā)送器蚀狰。
if (message is SendPort) {
message.send("我是從主isolate發(fā)送到子isolate的消息");
}
});
mainReceivePort.sendPort.send("我是從主isolate發(fā)來的消息");
// 創(chuàng)建一個(gè)子isolate
Isolate.spawn((SendPort mainSendPort) {
// 用主isolate發(fā)來的發(fā)送器發(fā)送消息就能夠在主isolate中接收到消息
mainSendPort.send("我是從子isolate發(fā)送到主isolate的消息");
// 如果我們需要從主isolate向子isolate中發(fā)送消息,那就需要在子isolate中創(chuàng)建一個(gè)自己的接收器
var childReceivePort = ReceivePort();
// 同樣的职员,給子isolate中的接收器設(shè)置監(jiān)聽
childReceivePort.listen((message) {
print("子isolate接收器接收到消息:$message");
});
// 將子isolate中的發(fā)送器發(fā)送給主isolate麻蹋,那樣就完成了雙向連接
var childSendPort = childReceivePort.sendPort;
mainSendPort.send(childSendPort);
}, mainReceivePort.sendPort); // 將消息接收器中的發(fā)送器發(fā)送給子isolate
}
ReceivePort不用時(shí)需要關(guān)閉,否則程序不會(huì)結(jié)束焊切。
receivePort.close();
2 任務(wù)隊(duì)列
在dart中是由任務(wù)驅(qū)動(dòng)的扮授。
同android中handler類似,在dart運(yùn)行環(huán)境中也是靠事件驅(qū)動(dòng)的专肪,通過event loop不停的從隊(duì)列中獲取消息或者事件來驅(qū)動(dòng)整個(gè)應(yīng)用的運(yùn)行刹勃,isolate發(fā)過來的消息就是通過loop處理。
但是不同的是在android中每個(gè)線程只有一個(gè)looper所對(duì)應(yīng)的messageQueue嚎尤,
而dart中有兩個(gè)隊(duì)列荔仁,一個(gè)叫做event queue(事件隊(duì)列),另一個(gè)叫做microtask queue(微任務(wù)隊(duì)列)
looper會(huì)先從微任務(wù)隊(duì)列中獲取任務(wù)芽死,直到獲取完乏梁,才去執(zhí)行事件隊(duì)列中的任務(wù)。
每當(dāng)執(zhí)行完事件隊(duì)列中的一個(gè)任務(wù)時(shí)关贵,又會(huì)去檢查微任務(wù)隊(duì)列中是否有了任務(wù)遇骑,如果有,先去執(zhí)行微任務(wù)揖曾。微任務(wù)的執(zhí)行優(yōu)先級(jí)比事件隊(duì)列任務(wù)高
void main() {
// 在dart中是由任務(wù)驅(qū)動(dòng)的落萎。
// 同android中handler類似亥啦,在dart運(yùn)行環(huán)境中也是靠事件驅(qū)動(dòng)的,通過event loop不停的從隊(duì)列中獲取消息或者事件來驅(qū)動(dòng)整個(gè)應(yīng)用的運(yùn)行练链,isolate發(fā)過來的消息就是通過loop處理翔脱。
// 但是不同的是在android中每個(gè)線程只有一個(gè)looper所對(duì)應(yīng)的messageQueue,
// 而dart中有兩個(gè)隊(duì)列兑宇,一個(gè)叫做event queue(事件隊(duì)列)碍侦,另一個(gè)叫做microtask queue(微任務(wù)隊(duì)列)
// looper會(huì)先從微任務(wù)隊(duì)列中獲取任務(wù),直到獲取完隶糕,才去執(zhí)行事件隊(duì)列中的任務(wù)。
// 每當(dāng)執(zhí)行完事件隊(duì)列中的一個(gè)任務(wù)時(shí)站玄,又會(huì)去檢查微任務(wù)隊(duì)列中是否有了任務(wù)枚驻,如果有,先去執(zhí)行微任務(wù)株旷。微任務(wù)的執(zhí)行優(yōu)先級(jí)比事件隊(duì)列任務(wù)高
print("開始執(zhí)行main方法--- ${DateTime.now()}");
var receivePort = ReceivePort();
receivePort.listen((message) {
print("執(zhí)行$message ${DateTime.now()}");
});
receivePort.sendPort.send("事件任務(wù)1");
Future.microtask(() {
// 執(zhí)行微任務(wù)
print("執(zhí)行微任務(wù)1 ${DateTime.now()}");
// 這邊休眠2s再登,會(huì)阻塞。又佐證了在dart中是單線程的晾剖。
sleep(const Duration(seconds: 2));
});
receivePort.sendPort.send("事件任務(wù)2");
Future.microtask(() {
// 執(zhí)行微任務(wù)
print("執(zhí)行微任務(wù)2 ${DateTime.now()}");
});
receivePort.sendPort.send("事件任務(wù)3");
// 如果在這里休眠3s锉矢,那上面的任務(wù)隊(duì)列中的任務(wù)也都要在3s后才執(zhí)行
// 只有當(dāng)main任務(wù)執(zhí)行完成之后,才會(huì)執(zhí)行任務(wù)隊(duì)列中的任務(wù)
sleep(const Duration(seconds: 3));
print("main方法執(zhí)行結(jié)束齿尽。沽损。。 ${DateTime.now()}");
}
上面的執(zhí)行打印結(jié)果:
開始執(zhí)行main方法--- 2022-03-23 16:35:37.269329
main方法執(zhí)行結(jié)束循头。绵估。。 2022-03-23 16:35:40.287288 ----------------3s后才開始執(zhí)行任務(wù)隊(duì)列中的任務(wù)
執(zhí)行微任務(wù)1 2022-03-23 16:35:40.288894 -------------------------------執(zhí)行微任務(wù)1卡骂,2s后才開始繼續(xù)執(zhí)行任務(wù)
執(zhí)行微任務(wù)2 2022-03-23 16:35:42.291514
執(zhí)行事件任務(wù)1 2022-03-23 16:35:42.294005
執(zhí)行事件任務(wù)2 2022-03-23 16:35:42.294192
執(zhí)行事件任務(wù)3 2022-03-23 16:35:42.294231
3 Future
future意為未來国裳,就是將來要執(zhí)行的事情。需要main方法執(zhí)行結(jié)束后才能執(zhí)行全跨,所以不會(huì)造成阻塞缝左。
void main() {
// 延遲處理的消息。至少3s后才能執(zhí)行浓若。
Future.delayed(Duration(seconds: 1), () {
// 讀取路徑下文件的內(nèi)容渺杉,返回的結(jié)果類型Future<String>
Future<String> readAsString =
File(r"/Users/phoenix/Downloads/test1").readAsString();
// 通過then來獲取到泛型的值。then也有返回值FutureOr<R>七嫌,F(xiàn)utureOr是可以返回void少办,也可以返回Future<R>
// 這個(gè)是模擬返回一個(gè)int值。int值可以繼續(xù)通過then來獲取诵原。
Future<int> then = readAsString.then((value) {
print("value=$value");
return 100;
});
// 其實(shí)我們可以寫成鏈?zhǔn)秸{(diào)用
File(r"/Users/phoenix/Downloads/test1").readAsString().then((value) {
print("鏈?zhǔn)秸{(diào)用 value=$value");
});
});
}
4 Stream
Stream(流)在dart中也經(jīng)常出現(xiàn)英妓,表示發(fā)出的一些列的異步數(shù)據(jù)挽放。
Future表示稍后獲得的一個(gè)數(shù)據(jù),所有的異步的操作的返回值都用future來表示蔓纠。但是Future只能表示一次異步獲得的數(shù)據(jù)辑畦。而Stream表示多次異步獲得的數(shù)據(jù)。
比如IO處理的時(shí)候腿倚,每次只會(huì)讀取一部分的數(shù)據(jù)和一次性讀取整個(gè)文件相比纯出,Stream的好處是處理過程中內(nèi)存占用較小。而File.readString()敷燎,是一次性讀取整個(gè)文件的內(nèi)容暂筝,文件很大的話會(huì)導(dǎo)致內(nèi)存占用過大的問題
import 'dart:io';
void main() {
// 通過流的方式來讀寫
File file = File(r"/Users/phoenix/Downloads/text.epub");
// openRead([int? start, int? end]) 可以傳入讀取的坐標(biāo),也可以不傳
// 返回的結(jié)果就是對(duì)應(yīng)流的集合
Stream<List<int>> openRead = file.openRead();
// 設(shè)置讀的監(jiān)聽
var listen = openRead.listen((event) {
// 一次讀取65536硬贯。讀取大文件時(shí)焕襟,有可能會(huì)讀取多次
print("listen(),會(huì)調(diào)用多次 ${event.length}");
});
// onData 可以替換掉listen方法
listen.onData((data) {
print("onData(),替換掉listen(),會(huì)調(diào)用多次");
});
// 文件讀完畢之后的回調(diào)
listen.onDone(() {
print("文件都讀完了");
});
// 也可以控制流暫停和重啟
// listen.pause();
// listen.resume();
}
通過stream進(jìn)行讀寫操作
void main() {
// 通過流的方式來讀寫
File file = File(r"/Users/phoenix/Downloads/text.epub");
File outFile = File(r"/Users/phoenix/Downloads/text2.epub");
// openRead([int? start, int? end]) 可以傳入讀取的坐標(biāo),也可以不傳
// 返回的結(jié)果就是對(duì)應(yīng)流的集合
Stream<List<int>> openRead = file.openRead();
var openWrite = outFile.openWrite();
// 方式一:我們可以直接全部寫入
openWrite.addStream(openRead);
// 方式二:通過設(shè)置讀的監(jiān)聽饭豹,分段多次寫入
var listen = openRead.listen((event) {
openWrite.add(data);
});
}
stream不能直接設(shè)置多個(gè)監(jiān)聽鸵赖,那怎么設(shè)置多個(gè)監(jiān)聽呢?
方式1:通過BroadcastStream
void main() {
var openRead = File(r"/Users/phoenix/Downloads/test1").openRead();
// 調(diào)用多次會(huì)報(bào)以下異常
// FileSystemException: An async operation is currently pending, path = '/Users/phoenix/Downloads/test1'
// openRead.listen((event) {});
// openRead.listen((event) {});
// 那怎么設(shè)置多個(gè)監(jiān)聽呢拄衰?通過廣播模式
var asBroadcastStream = openRead.asBroadcastStream();
asBroadcastStream.listen((event) {
print("listen1 event=$event");
});
asBroadcastStream.listen((event) {
print("listen2 event=$event");
});
}
方式1:通過StreamController
void main() {
var streamController = StreamController.broadcast();
// 在發(fā)送消息之前設(shè)置監(jiān)聽它褪,可以接收到廣播
streamController.stream.listen((event) {
print("1....接收 $event");
});
// 發(fā)送消息
streamController.add("消息1");
// 在發(fā)送消息之后才去設(shè)置監(jiān)聽,不可以接收到廣播
streamController.stream.listen((event) {
print("2....接收 $event");
});
}
5 async-await
使用async和await的代碼都是異步的翘悉,但是看起來很像同步代碼茫打。當(dāng)我們需要獲取A的結(jié)果,再執(zhí)行B時(shí)镐确,你需要then()->then()包吝,但是利用async與await能夠很好的解決回調(diào)問題。
-
async
被async修飾的方法就變成了異步方法源葫。返回值是Future或者void诗越。可以通過then獲取到方法的返回值息堂。
Future<String> readText() async { Future<String> readAsString = File(r"/Users/phoenix/Downloads/test1").readAsString(); return readAsString; }
獲取返回值:
void main() { readText().then((value) { print("讀取文本:$value"); }); }
-
await
await可以將原本異步的操作變成同步操作嚷狞。
Future<String> readText() async { // 如果不寫await關(guān)鍵字,則readAsString()是一個(gè)異步操作荣堰。在第一個(gè)沒有讀取完之后就開始執(zhí)行第二方法了床未。 // Future<String> readAsString = File(r"/Users/phoenix/Downloads/test1").readAsString(); // var readAsString2 = File(r"/Users/phoenix/Downloads/test1").readAsString(); // await 等待future執(zhí)行完成后再執(zhí)行后續(xù)代碼 // 使用了await關(guān)鍵字,則這段代碼就變成同步振坚,會(huì)阻塞后續(xù)代碼的執(zhí)行 var readAsString = await File(r"/Users/phoenix/Downloads/test1").readAsString(); var readAsString2 = await File(r"/Users/phoenix/Downloads/test1").readAsString(); return readAsString + readAsString2; } void main() { readText().then((value) { print("讀取文本:$value"); }); }