能否把现有的StreamController转换为SendPort,以便与Isolate进行通信?

移动开发 2026-07-09

我正在重构一个Flutter类,使其在一个与UI分离的 Isolate 中运行。该类在构造时已经接收两个用于与调用方通信的 StreamController,一个用于输入,一个用于输出。重构之前,与调用方的通信在这里提供的简单代码中就能正常工作,在我的应用中也一样(其中流由 StreamBuilders消费)。

我本以为可以重用在调用方构造的 StreamController,以在单独的Isolate中与对象通信,从而避免重复代码,但这并不奏效。没有异常也没有错误。消息根本没传过来。

文档(https://dart.dev/language/isolates#receiveport-and-sendport)指出:

端口的行为与Stream对象类似(事实上,接收端口实现了Stream!)。你可以把SendPort和 ReceivePort看作是Stream的 StreamController和监听器,分别对应。SendPort就像一个StreamController,因为你可以用SendPort.send() 向它们“添加”消息,而这些消息由监听器处理,在本例中即为ReceivePort。ReceivePort再通过把它收到的消息作为参数传递给你提供的回调来处理。

我的问题是,是否可以以某种方式把我现有的 StreamController 转换成 SendPortReceivePort,从而避免重复代码?

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

void main() {
  // set it to true to run in Isolate
  const bool RUN_ISOLATE = true;

  // inSC: sending into Heavy
  final StreamController<HeavyMsg> inSC = StreamController<HeavyMsg>();
  // outSC: receiving from Heavy
  final StreamController<HeavyMsg> outSC = StreamController<HeavyMsg>();

  if (RUN_ISOLATE) {
    // subscribe to Heavy's output Stream and print what it sends
    outSC.stream.listen((msg) {
      print("main_isolate() : got message from Heavy: ${msg}");
    });
    // send it a message
    inSC.sink.add(HeavyMsg("a message from main_isolate()"));
    Heavy.constructAndGoIsolate(inSC, outSC);
  } else {
    // subscribe to Heavy's output Stream and print what it sends
    outSC.stream.listen((msg) {
      print("main_plain() : got message from Heavy: ${msg}");
    });
    Heavy hobj = Heavy(inSC, outSC);
    inSC.sink.add(HeavyMsg("a message from main_plain()"));
    hobj.go();
  }
}

class Heavy {
  // Stream for receiving from caller
  final StreamController<HeavyMsg> inSC;

  // Stream for sending to caller
  final StreamController<HeavyMsg> outSC;

  /* Constructor, parameters are the 2 StreamController's
     for communicating with the caller.
     The StreamControllers are constructed in the caller */
  Heavy(this.inSC, this.outSC) {
    print("Heavy() : constructor called ...");
    inSC.stream.listen((msg) {
      print("Heavy: received this message: ${msg}");
    });
  }

  /* the heavy calculations are done here and before, during and after
     messages are exchanged with the caller */
  void go() {
    print("Heavy.go() : called your should receive some data ...");
    outSC.sink.add(HeavyMsg('starting the computation'));
    /* insert heavy computation */
    outSC.sink.add(HeavyMsg('finished the computation'));
  }

  /* above this point is the original functionality without Isolates */

  /* below this point is the refactoring code added
     to using Isolates uses these two methods */
  static Future<void> constructAndGoIsolate(
      StreamController<HeavyMsg> inSC, StreamController<HeavyMsg> outSC) async {
    await Isolate.spawn<IsolateParams>(
        _constructAndGoIsolateHelper, IsolateParams(inSC, outSC));
  }

  static void _constructAndGoIsolateHelper(IsolateParams params) {
    Heavy hobj = Heavy(params.inSC, params.outSC);
    params.outSC.sink
        .add(HeavyMsg("a message from _constructAndGoIsolateHelper()"));
    hobj.go();
  }
}

/* The Message class for communicating via the Stream with the class */
class HeavyMsg {
  final String msg;

  HeavyMsg(this.msg);

  @override
  toString() {
    return "HeavyMsg: ${msg}";
  }
}

/* Class to provide parameters to the spawned Isolate function */
class IsolateParams {
  // basically we are supplying two StreamController's for communicating
  // with the caller
  final StreamController<HeavyMsg> inSC;
  final StreamController<HeavyMsg> outSC;

  IsolateParams(this.inSC, this.outSC);
}

附言:上述代码可以通过命令行使用 dart x.dart 运行。

解决方案

你可以使用来自 stream_channel 包的 IsolateChannel 类。

示例:

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

import 'package:multitasking/multitasking.dart';
import 'package:multitasking/work/isolated_work.dart';
import 'package:stream_channel/isolate_channel.dart';

Future<void> main() async {
  var recv = ReceivePort();
  var recvChannel = IsolateChannel<String>.connectReceive(recv);
  final sink = recvChannel.sink;
  final stream = recvChannel.stream;
  final sendPort = recv.sendPort;
  final work = IsolatedWork.withArgument(sendPort, _computation);

  unawaited(() async {
    try {
      await work.run();
    } on CancellationException {
      print('IsolatedWork terminated');
    }
  }());

  sink.add('Hello, I am parent');
  stream.listen((event) {
    print(event);
    if (event.contains('Hello')) {
      sink.add('Goodbye child');
    }
  });

  Timer(Duration(seconds: 2), () {
    print('Terminating work');
    work.terminate(force: true);
    print('Closing everything');
    recv.close();
    sink.close();
  });
}

Future<void> _computation(SendPort sendPort) async {
  var sendChannel = IsolateChannel<String>.connectSend(sendPort);
  final sink = sendChannel.sink;
  final stream = sendChannel.stream;
  stream.listen((event) {
    print(event);
    if (event.contains('Hello')) {
      sink.add('Hello, I am child');
    } else if (event.contains('Goodbye')) {
      sink.add('Goodbye parent');
    }
  });

  await Completer<void>().future;
}

结果:

Hello, I am parent
Hello, I am child
Goodbye child
Goodbye parent
Terminating work
Closing everything
IsolatedWork terminated
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章