如何在 Dart 中使用 Isolate 实现子线程与主线程通信?

新手上路,请多包涵

Isolate相关的问题

最近在学flutter,所以刚接触了dart,遇到了一个关于isolate的问题:
我想实现一个简单的案例,子线程给主线程发送一些数据(包含'hello'字符串),当主线程收到'hello'会给子线程回复'hi',由于想直观的看到运行结果我加了sleep。但是实际的运行结果并没有如我想象的一样,代码如下:

void main(){
  var rece = new ReceivePort();
  Isolate.spawn((sendPort) {
    var sonRece = new ReceivePort();
    sendPort.send(sonRece.sendPort);
    sonRece.listen((message) {
      print("子线程收到信息:$message");
    });

    for (var i = 0; i < 10; i++) {
      sleep(Duration(seconds: 2));
      if (i == 6) {
        sendPort.send("hello");
        continue;
      }
      sendPort.send(i);
    }
  }, rece.sendPort);

  //主线程监听
  rece.listen((message) {
    var sendPort;
    if (message is SendPort) {
      sendPort = message;
    }
    print("主线程收到信息:$message");
    if (message == "hello") {
      sendPort?.send("hi");
    }
  });
}

运行结果:
image.png
代码运行结果为什么没有打印'子线程收到信息:hi'呢?

阅读 361
avatarAI BotBETA

回答

在你的 Dart 代码中,子线程没有打印 "子线程收到信息:hi" 的原因是消息发送和接收的逻辑处理有误。具体问题是,你在主线程中设置的 sendPort 变量只在第一次接收到 SendPort 对象时赋值,而后续的消息处理没有重新检查消息类型,导致在接收到 "hello" 并回复 "hi" 后,子线程并没有正确地设置或保持一个有效的 sendPort 引用用于接收来自主线程的消息。

要解决这个问题,你需要确保子线程能够持续监听并正确处理来自主线程的消息。以下是一个修正后的代码示例:

import 'dart:async';
import 'dart:isolate';

void main() {
  var rece = new ReceivePort();
  Isolate.spawn((sendPort) {
    var sonRece = new ReceivePort();
    sendPort.send(sonRece.sendPort);

    var parentSendPort = sendPort; // 保持对父线程发送端口的引用
    sonRece.listen((message) {
      print("子线程收到信息:$message");
      if (message == "hi") {
        // 这里可以添加更多处理逻辑
      }
    });

    for (var i = 0; i < 10; i++) {
      sleep(Duration(seconds: 2));
      if (i == 6) {
        parentSendPort.send("hello");
      } else {
        parentSendPort.send(i);
      }
    }
  }, rece.sendPort);

  // 主线程监听
  rece.listen((message) {
    if (message is SendPort) {
      var sendPort = message;
      print("主线程收到子线程的发送端口");
      sendPort.send("确认收到发送端口"); // 可选,用于测试连接

      // 创建一个新的监听器用于处理子线程的消息
      var parentRece = new ReceivePort();
      sendPort.send(parentRece.sendPort); // 将主线程的接收端口发送给子线程(这一步是可选的,取决于你的通信需求)

      parentRece.listen((msgFromChild) {
        print("主线程收到信息:$msgFromChild");
        if (msgFromChild == "hello") {
          sendPort.send("hi");
        }
      });
    }
  });
}

注意:上面的代码进行了一些假设性的修改,比如添加了 parentSendPort 来保持对父线程发送端口的引用,并在主线程中创建了一个新的 ReceivePort 来专门处理子线程的消息。然而,这个示例代码可能不是最优的解决方案,因为它在逻辑上有些复杂且可能不是最直接的方式来处理线程间通信。

在实际应用中,你可能需要简化通信逻辑,确保每个 Isolate 都有清晰的发送和接收逻辑,并且正确地管理消息传递。此外,考虑到 Dart 的 Isolate 模型和 Flutter 的 UI 线程(即主线程)之间的交互,你可能还需要进一步调整代码以适应 Flutter 的事件循环和 UI 更新机制。

如果你的目标是学习 Dart 的 Isolate 和线程间通信,建议从更简单的例子开始,并逐步增加复杂性。同时,阅读 Dart 和 Flutter 的官方文档也是非常重要的,它们提供了关于如何使用 Isolate 和处理并发编程的详细指南。

2 个回答

把SendPort 放外面:

void main() {
  var rece = new ReceivePort();
  SendPort? childSendPort; 
  
  Isolate.spawn((sendPort) {
    var sonRece = new ReceivePort();
    sendPort.send(sonRece.sendPort);
    
    sonRece.listen((message) {
      print("子线程收到信息:$message");
    });

    for (var i = 0; i < 10; i++) {
      sleep(Duration(seconds: 2));
      if (i == 6) {
        sendPort.send("hello");
        continue;
      }
      sendPort.send(i);
    }
  }, rece.sendPort);

  rece.listen((message) {
    if (message is SendPort) {
      childSendPort = message; 
    } else {
      print("主线程收到信息:$message");
      if (message == "hello") {
        childSendPort?.send("hi"); 
      }
    }
  });
}

建议用异步代替 sleep,这样更不会堵:

void main() {
  var rece = new ReceivePort();
  SendPort? childSendPort;
  
  Isolate.spawn((sendPort) async {
    var sonRece = new ReceivePort();
    sendPort.send(sonRece.sendPort);
    
    sonRece.listen((message) {
      print("子线程收到信息:$message");
    });

    for (var i = 0; i < 10; i++) {
      await Future.delayed(Duration(seconds: 2));
      if (i == 6) {
        sendPort.send("hello");
        continue;
      }
      sendPort.send(i);
    }
  }, rece.sendPort);

  rece.listen((message) {
    if (message is SendPort) {
      childSendPort = message;
    } else {
      print("主线程收到信息:$message");
      if (message == "hello") {
        childSendPort?.send("hi");
      }
    }
  });
}

可以通过将sendPort声明在外部作用域来解决

主线程收到信息:SendPort
主线程收到信息:0
主线程收到信息:1
主线程收到信息:2
主线程收到信息:3
主线程收到信息:4
主线程收到信息:5
主线程收到信息:hello
子线程收到信息:hi
主线程收到信息:7
主线程收到信息:8
主线程收到信息:9
import 'dart:isolate';
import 'dart:async';

void main() {
  var mainReceivePort = ReceivePort();
  Isolate.spawn(isolateEntry, mainReceivePort.sendPort);

  SendPort? isolateSendPort;

  mainReceivePort.listen((message) {
    if (message is SendPort) {
      isolateSendPort = message;
    } else {
      print("主线程收到信息:$message");
      if (message == "hello") {
        isolateSendPort?.send("hi");
      }
    }
  });
}

void isolateEntry(SendPort mainSendPort) async {
  var isolateReceivePort = ReceivePort();
  mainSendPort.send(isolateReceivePort.sendPort);

  isolateReceivePort.listen((message) {
    print("子线程收到信息:$message");
  });

  for (var i = 0; i < 10; i++) {
    await Future.delayed(Duration(seconds: 2));
    if (i == 6) {
      mainSendPort.send("hello");
      await Future.delayed(Duration(milliseconds: 100)); // 确保 "hi" 在 "hello" 之后发送
    } else {
      mainSendPort.send(i);
    }
  }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏