如何在Dart中更新长运行Isolate内的值?
解决方案
要在持续运行的while循环中接收SendPort发送的值,你可以通过以下方式实现:
- 用变量缓存最新值:在Isolate任务内定义一个变量存储最新收到的命令值;
- 监听消息流更新变量:通过
listen方法监听命令端口的消息,收到新值时直接更新缓存变量,这个操作不会阻塞循环; - 替换阻塞的sleep为非阻塞延迟:
sleep会完全阻塞Isolate的事件循环,导致消息无法被处理,必须换成await Future.delayed,让事件循环有机会处理消息队列。
修改后的完整代码:
void main() async { final ReceivePort p = ReceivePort(); var events = StreamQueue<dynamic>(p); // 修正顺序:先启动Isolate,再获取sendPort(原顺序会导致主线程一直等待) await Isolate.spawn(longTask, p.sendPort); SendPort sendPort = await events.next; sendPort.send(30); Future.delayed(const Duration(seconds: 10), () { sendPort.send(10); }); Future.delayed(const Duration(seconds: 50), () { sendPort.send(30); }); } Future<void> longTask(SendPort p) async { final commandPort = ReceivePort(); p.send(commandPort.sendPort); int? latestValue; // 监听命令端口,收到消息就更新最新值 commandPort.listen((message) { if (message is int) { latestValue = message; print("收到新值: $latestValue"); } }); // 持续循环,每秒执行一次 while (true) { // 在这里使用最新值 if (latestValue != null) { print("当前使用的值: $latestValue"); } else { print("暂无命令值"); } // 用非阻塞的延迟替代sleep,让事件循环能处理消息 await Future.delayed(const Duration(seconds: 1)); } }
关键细节说明:
- 修正Isolate启动顺序:原代码中先等待
events.next再启动Isolate,会导致主线程一直等待(因为Isolate还未启动发送sendPort),所以必须先调用Isolate.spawn再获取sendPort; - listen的非阻塞特性:
listen是异步监听,不会阻塞当前的while循环,新消息到来时会触发回调更新变量; - 替换sleep为Future.delayed:
sleep属于阻塞式延迟,会暂停Isolate的所有操作(包括消息处理);而await Future.delayed会释放事件循环,让Isolate可以处理收到的消息,之后再继续循环。
内容的提问来源于stack exchange,提问作者Dwiyi
相关产品推荐
相关产品推荐

