在我们传统的理解中,通讯应该是这个样子的,比如A和B之间通讯,A和B应该各自有个发件箱和一个收件箱,然后A通过A的发件箱将信息发送到B的收件箱内,同样B通过B的发件箱将信息发送到A的发件箱,如图:
image

但是在isolate中的通讯不是这样的,它的通讯模型比较特别.首先你要进程间通讯,你的每个进程必须要有个通讯用的收件箱,在isolate中这个收件箱类叫做ReceivePort,这个名字本身没什么问题,但是在理解模型时很不方便,所以你可以先把这个ReceivePort理解为一个收件箱,这个收件箱自带一个发件箱(ReceivePort中有个sendPort属性,为了方便理解,你可以把看到的port单词直接理解为box).所以我们要做的第一步就是创建这个收件箱

1
var mainReciveBox = ReceivePort();

这个语句会自动创建一个这个语句所在线程的收件箱,比如你在main()函数里执行这个语句,那么这里创建的就是主线程的收件箱,假如现在我们在A线程里创建了这个收件箱,由于前面说过这个收件箱自带发件箱,所以A也有了一个发件箱
image

好了,现在我们创建一个线程B(具体创建方法等后面再说),同样我们在线程B里也创建一个属于线程B的收件箱,这样A和B两个线程都各自拥有了自己的收件箱和发件箱.
image

接下来就是比较特别的地方了,只要记住一个原则就比较容易理解了,就是自己的收件箱只能收到自己发件箱发来的消息,这个很重要,是理解这个通讯机制的关键.基于这个前提,那么假如B要给A发消息怎么办?因为A的收件箱只能收到自己A的发件箱发来的消息,所以解决的办法就是在创建线程B的时候创建一个A发件箱的副本传给B,这样就等于B拥有了一个A的发件箱,所以B就可以给A发消息了,同样,如果A要发消息给B,当B有了自己的发件箱后要第一个时间把自己的发件箱发一个给A,这样A和B之间就能相互通信了
image

主线程代码

1
2
3
4
5
6
7
8
9
10
11
12
main() async {
//创建主线程的收件箱
var mainReciveBox = ReceivePort();
//收件箱里自带的发件箱,可以看看他的hashCode
print('主线程发件箱 的sendPort:${mainReciveBox.sendPort.hashCode}');
// 创建一个子线程,需要主线程的发件箱,
// subThread是子线程的入口函数
await Isolate.spawn<SendPort>(subThread, mainReciveBox.sendPort);
}

子线程代码

1
2
3
4
5
6
7
8
9
10
11
12
13
subThread(SendPort mainSendBoxCopy) async {
// 通过比较hashCode可以看到,这里的参数mainSendBoxCopy就是主线程的发件箱
// 拥有了这个发件箱就可以给主线程发消息了
print('子isolate接收到 的sendPort:${mainSendBoxCopy.hashCode}');
// 要实现相互通信,子线程也要有自己的收件箱
var subReciveBox = new ReceivePort();
// 同样可以看看子线程发件箱的hashCode
print('创建子isolate 的sendPort:${subReciveBox.sendPort.hashCode}');
// 前面说过,要实现互相通信,子线程有了自己的发件箱之后要第一个时间把发件箱发送一个给主线程
mainSendBoxCopy.send(subReciveBox.sendPort);
}

最后我们把完整的消息交互过程补上
主线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
main() async {
//创建主线程的收件箱
var mainReciveBox = ReceivePort();
//收件箱里自带的发件箱,可以看看他的hashCode
print('主线程发件箱 的sendPort:${mainReciveBox.sendPort.hashCode}');
// 创建一个子线程,需要主线程的发件箱,
// subThread是子线程的入口函数
await Isolate.spawn<SendPort>(subThread, mainReciveBox.sendPort);
// 定义一个发件箱,准备用来存放子线程的发件箱
SendPort subSendBox;
//开始监听消息
mainReciveBox.listen((msg) {
// 收到了子线程发来的收件箱
if (msg is SendPort) {
subSendBox = msg;
// 收到了自线程的发件箱之后就可以向子线程发送消息了
subSendBox.send("hoho1");
subSendBox.send("hoho2");
subSendBox.send("hoho3");
} else {
print("主线程监听接收到的消息是:$msg");
}
});
}

子线程完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
subThread(SendPort mainSendBoxCopy) async {
// 通过比较hashCode可以看到,这里的参数mainSendBoxCopy就是主线程的发件箱
// 拥有了这个发件箱就可以给主线程发消息了
print('子isolate接收到 的sendPort:${mainSendBoxCopy.hashCode}');
// 要实现相互通信,子线程也要有自己的收件箱
var subReciveBox = new ReceivePort();
// 同样可以看看子线程发件箱的hashCode
print('创建子isolate 的sendPort:${subReciveBox.sendPort.hashCode}');
// 前面说过,要实现互相通信,子线程有了自己的发件箱之后要第一个时间把发件箱发送一个给主线程
mainSendBoxCopy.send(subReciveBox.sendPort);
//再发些别的数据给主线程
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据1");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据2");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据3");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据4");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据5");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据6");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据7");
mainSendBoxCopy.send("来自子线程向父线程发送的消息 发送数据8");
// 等待接受主线程的消息
await for (var msg in subReciveBox) {
print("子isolate收到的消息:$msg");
}
}

结果:
创建父isolate 的sendPort:232827375
子isolate接收到 的sendPort:232827375
创建子isolate 的sendPort:440139342
在主线程中收到了子线程的发件箱440139342
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据1
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据2
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据3
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据4
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据5
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据6
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据7
父ioslate 监听接收到的消息是:来自子线程向父线程发送的消息 发送数据8
子isolate收到的消息:hoho1
子isolate收到的消息:hoho2
子isolate收到的消息:hoho3

到这里就是先了现在之间的互相通讯了
image

大家有没觉得并不是那么称手,下回有空说一下稍微进阶一点的用法

原创文章,转载请注明出处,谢谢!(感谢同事柏的帮助)