LoginSignup
6
3

More than 5 years have passed since last update.

Dartのアイソレート間で相互通信をする例

Last updated at Posted at 2018-03-26

DartのIsolateを使って相互に通信をする例。

追記あり

Isolate→spawn元(main)の方向の通信は、ReceivePort.sendPortをspawn時に渡してやれば良いが、逆方向の通信(main→Isolate)は、spawnしたIsolate先でReceivePortを作成し、そのsendPortをmainに戻してもらい、そのsendport経由で送信することになる。

sendPortをどうやって戻すかだが、以下ではIsolateに渡すReceivePort経由で受けとっている。(これ以外の良い方法は、グローバル変数にするとか以外にあるかな。どなたかご存知の方教えて!)

そのとき、sendPortをStream.firstで受けとろうとすると、Stream.firstはSteramをlistenし受信後にsubscriptionをcancelしてしまうので、listenerを一回限りしか設定できないsingle-subscription streamでは継続受信できなくなってしまう。なのでStream. asBroadcastStreamでbroadcast streamにしておく必要がある。

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

main() async {
  try {
    var singleChannel = new ReceivePort();
    await Isolate.spawn(echo, singleChannel.sendPort);

    var multiChannel = singleChannel.asBroadcastStream();
    var callbackSendPort = await multiChannel.first;
    multiChannel.listen((msg) {
      print('main: received ${msg}');
      callbackSendPort.send('hoy!');
    });
  } catch (e) {
    print('error ${e}');
  }
}

// the entry point for the isolate
void echo(sendPort) async {

  var callBackReceivePort = new ReceivePort();
  sendPort.send(callBackReceivePort.sendPort);
  callBackReceivePort.listen((msg){
    print('echo: received ${msg}');
  });
  while (true) {
    await new Future.delayed(new Duration(seconds: 1));
    sendPort.send("hello");
  }
}

結果

% dart web/index2.dart
main: received hello
echo: received hoy!
main: received hello
echo: received hoy!
main: received hello
echo: received hoy!
:

sendPortを送り戻してもらう他の方法としては、firstを使わずにストリームで最初に受けとる要素として取り出すという方法もあるだろう。
こんな感じ。

    var callbackSendPort = null;
    multiChannel.listen((msg) {
      if (callbackSendPort == null) {
        callbackSendPort = msg;
      }
      else {
        print('main: received ${msg}');
        callbackSendPort.send('hoy!');
      }
    });

これが嫌なのはまちがいない1が、asBroadcastStreamも相当である。良い方法があればおしえてください。

追記

https://www.cresc.co.jp/tech/java/Google_Dart/DartLanguageGuide.pdf
18.5節 アイソレート間の通信リンクの確立

を見ると、後者の方法に近く、ただ

    var callbackSendPort = null;
    multiChannel.listen((msg) {
      if (msg is SendPort) {
        callbackSendPort = msg;
      }
      else {
        print('main: received ${msg}');
        callbackSendPort.send('hoy!');
      }
    });

に相当するような、実行時の型で切りわけをしていますね。また接続状態を状態遷移でしっかりと管理している。

追記2

StreamIteratorを使うのが良いみたい。asBroadcastStream()が不要であり、型も気持ち守れて、状態変数を導入しなくてよい。

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

main() async {
  try {
    var channel = new ReceivePort();
    await Isolate.spawn(echo, channel.sendPort);

    StreamIterator itr = new StreamIterator(channel);
    if (await itr.moveNext()) {
       SendPort callbackSendPort = itr.current;
       while (await itr.moveNext()) {
         print('main: received ${itr.current}');
         callbackSendPort.send('hoy!');
       }
    }
  } catch (e) {
    print('error ${e}');
  }
}

// the entry point for the isolate
Future echo(sendPort) async {

  var callBackReceivePort = new ReceivePort();
  sendPort.send(callBackReceivePort.sendPort);
  callBackReceivePort.listen((msg){
    print('echo: received ${msg}');
  });
  while (true) {
    await new Future.delayed(new Duration(seconds: 1));
    sendPort.send("hello");
  }
}

こちらも参考に
https://speakerdeck.com/uehaj/dart-isolate-port-and-capabilities


  1. 型制約できなくなる気がする。Dartにユニオン型ってあったっけ。 

6
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3