You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Dart中exitPort处理器的if语句未在所有Isolate结束后执行?

Dart Isolate 提前触发exitPort关闭的原因分析

问题描述

预期printTest方法在所有Isolate执行完成后才被调用,但实际它在Isolate未全部结束时就已执行,导致Closing the exitPort输出在中间而非末尾。程序通过if (--liveIsolates == 0)判断Isolate是否全部执行完毕,同时用Test类实例验证不同Isolate内的实例一致性。

源码

import "dart:isolate";

Test test = Test();

void main(final List<String> args) async {
  final exitPort = RawReceivePort();
  const int numberOfIsolates = 1000;
  int liveIsolates = 0;

  exitPort.handler = (_) {
    print("Entering handler");

    final alpha = "abcdefghijklmnopqurstuvwxyz";
    print("$alpha $alpha $alpha $_");
    if (--liveIsolates == 0) {
      printTest();
      print("Closing the exitPort");
      exitPort.close();
    }

    print("Exiting handler");
  };

  liveIsolates = numberOfIsolates;

  for (int i = 0; i < numberOfIsolates; i++) { // 创建多个Isolate
    final isolate1 =
        await Isolate.spawn(foo, exitPort.sendPort, onExit: exitPort.sendPort);
  }

  print("Exiting main");
}

void foo(final SendPort sendPort) {
  sendPort.send("Data to be send from foo");
  print("Entering foo sendPort id ${sendPort.hashCode}");
  test++;
  print("Exiting foo id test : ${test.hashCode}");
  print("Exiting foo id test : $test");
}

void printTest() { // 应由exitPort处理器最后调用
  print("Value of test : $test");
}

class Test {
  int _data = 0;

  void set(int value) => _data = value;

  int get data => _data;

  @override
  String toString() {
    return _data.toString();
  }

  Test operator +(int _) { // 重载++运算符
    _data++;
    return this;
  }
}

核心原因:exitPort同时接收两类消息

你的exitPort被绑定了两种消息来源:

  • Isolate主动调用sendPort.send()发送的业务消息(来自foo函数里的sendPort.send("Data to be send from foo"))
  • Isolate退出时系统自动发送的onExit通知消息(来自Isolate.spawn的onExit: exitPort.sendPort参数)

每一条消息都会触发exitPort.handler执行,而你在handler里对liveIsolates做了--liveIsolates操作——不管收到的是业务消息还是退出消息。

当创建1000个Isolate时,每个Isolate会发送2条消息:1条业务消息+1条退出消息,总共2000条消息。但你初始化liveIsolates = 1000,当第1000条消息(大概率是某个Isolate的业务消息)被处理时,--liveIsolates就会变成0,直接触发关闭逻辑,此时还有大量Isolate的消息没处理,自然会看到Closing the exitPort出现在输出中间。

额外说明:跨Isolate的全局变量无效

你用全局Test test实例验证Isolate实例一致性的逻辑不成立——Dart的Isolate是完全内存隔离的,每个Isolate会拥有独立的内存空间,全局变量test在每个Isolate里都是独立的副本,所以在foo里对test++的操作只会修改当前Isolate内的副本,主Isolate的test值始终是0,这也是printTest输出Value of test : 0的原因。

修复方案

要解决提前关闭的问题,需要区分业务消息和退出消息,比如用不同的Port分别处理:

import "dart:isolate";

Test test = Test();

void main(final List<String> args) async {
  final exitPort = RawReceivePort();
  final dataPort = RawReceivePort();
  const int numberOfIsolates = 1000;
  int liveIsolates = numberOfIsolates;

  // 只处理Isolate退出消息,才递减计数
  exitPort.handler = (_) {
    print("Isolate exited");
    if (--liveIsolates == 0) {
      printTest();
      print("Closing ports");
      exitPort.close();
      dataPort.close();
    }
  };

  // 单独处理业务消息
  dataPort.handler = (message) {
    print("Entering data handler");
    final alpha = "abcdefghijklmnopqurstuvwxyz";
    print("$alpha $alpha $alpha $message");
    print("Exiting data handler");
  };

  for (int i = 0; i < numberOfIsolates; i++) {
    // 业务消息发往dataPort,退出通知发往exitPort
    await Isolate.spawn(foo, dataPort.sendPort, onExit: exitPort.sendPort);
  }

  print("Exiting main");
}

void foo(final SendPort sendPort) {
  sendPort.send("Data to be send from foo");
  print("Entering foo sendPort id ${sendPort.hashCode}");
  Test localTest = Test(); // 每个Isolate的独立实例
  localTest++;
  print("Exiting foo id test : ${localTest.hashCode}");
  print("Exiting foo id test : $localTest");
}

void printTest() {
  print("Value of main isolate test : $test");
}

class Test {
  int _data = 0;

  void set(int value) => _data = value;

  int get data => _data;

  @override
  String toString() {
    return _data.toString();
  }

  Test operator +(int _) {
    _data++;
    return this;
  }
}

内容的提问来源于stack exchange,提问作者user8234870

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 21:10:31