【问题标题】:Dart Isolates As Workers飞镖隔离为工人
【发布时间】:2012-05-05 08:20:51
【问题描述】:

已编辑以使问题更清晰。

我正在尝试在 Dart 中使用 Isolates(或 Web Workers)。我能找到的在主线程和隔离线程之间进行通信的唯一方法是 sendcall & then 从主线程。但这是主线程将一些数据传递给隔离的好方法。

如果我想让隔离者成为生成信息的人怎么办?就像一个游戏引擎,它在工作线程中完成所有物理,然后将更新的世界信息发送到主线程?在 JavaScript 中,您可以随时发送数据。 Dart 中是否有有效的方法?还是我还要等主线程调用我,然后再传给它?

附:我想知道,call & then 是否会阻塞线程直到回复完成?

【问题讨论】:

    标签: communication dart dart-isolates


    【解决方案1】:

    这是一个示例,其中父进程创建了两个隔离,然后两个隔离也与父进程一起相互通信。

    父代码:

    import 'dart:isolate';
    import 'dart:html';
    import 'dart:async';
    
    main() {
      querySelector('#output').text = 'Your Dart app is running.';
      int counter = 0;
    
      // Parent - Child 1
      SendPort csendPort1;
      ReceivePort receivePort1 = new ReceivePort();
      // Parent - Child 2
      SendPort csendPort2;
      ReceivePort receivePort2 = new ReceivePort();
      // Child1 - Child2
      SendPort csendPort11;
      SendPort csendPort12;
    
      // Child 1
      receivePort1.listen((msg) {
        if (csendPort1 == null) {
          csendPort1 = msg;
        } else if (csendPort11 == null) {
          csendPort11 = msg;
        } else {
          print('$msg');`enter code here`
        }
      });
    
      bool child1 = false;
      Isolate.spawnUri(Uri.parse('child.dart'), [], receivePort1.sendPort).then((isolate) {
        print('Child 1 isolate spawned');
        new Timer.periodic(const Duration(milliseconds: 500), (t) {
          if (csendPort11 != null && csendPort12 != null && child1 == false) {
            child1 = true;
            csendPort12.send(csendPort11);
          } else {
            csendPort1.send('Parent-Child1: ${counter++}');
          }
        });
      });
    
      // Child 2
      receivePort2.listen((msg) {
        if (csendPort2 == null) {
          csendPort2 = msg;
        } else if (csendPort12 == null) {
          csendPort12 = msg;
        } else {
          print('$msg');
        }
      });
    
      bool child2 = false;
      Isolate.spawnUri(Uri.parse('child.dart'), [], receivePort2.sendPort).then((isolate) {
        print('Child 2 isolate spawned');
        new Timer.periodic(const Duration(milliseconds: 500), (t) {
          if (csendPort11 != null && csendPort12 != null && child2 == false) {
            child2 = true;
            csendPort11.send(csendPort12);
          } else {
            csendPort2.send('Parent-Child2: ${counter++}');
          }
        });
      });
    }
    

    子代码:

    import 'dart:isolate';
    import 'dart:async';
    
    int pcounter = 0;
    int ccounter = 0;
    
    SendPort csendPort;
    void handleTimeout() {
      csendPort.send("${ccounter++}");
    }
    
    main(List<String> args, SendPort psendPort) {
      // Parent Comm
      ReceivePort creceivePort1 = new ReceivePort();
      psendPort.send(creceivePort1.sendPort);
    
      creceivePort1.listen((msg) {
        psendPort.send('Child-Parent: ${pcounter++} - ${msg}');
      });
    
      // Child-Child Comm
      ReceivePort creceivePort2 = new ReceivePort();
      psendPort.send(creceivePort2.sendPort);
    
      creceivePort2.listen((msg) {
        if (csendPort == null) {
          csendPort = msg;
          csendPort.send("${ccounter++}");
        } else {
          print("Child-Child: $msg");
          var duration = const Duration(milliseconds: 2000);
          new Timer(duration, handleTimeout);
        }
      });
    }
    

    HTML 代码:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="scaffolded-by" content="https://github.com/google/stagehand">
        <title>WebIsolateTest</title>
        <link rel="stylesheet" href="styles.css">
        <script defer src="main.dart" type="application/dart"></script>
        <script defer src="packages/browser/dart.js"></script>
    </head>
    
    <body>
    
      <div id="output"></div>
    
    </body>
    </html>
    

    【讨论】:

    • 以上代码适用于 dart sdk 版本 1.14.2 以及 Chrome、Firefox 和 IE11
    【解决方案2】:

    从 Dart 1.0 开始,您可以像这样使用隔离:

    import 'dart:isolate';
    import 'dart:async';
    
    void doStuff(SendPort sendPort) {
      print('hi from inside isolate');
      ReceivePort receivePort = new ReceivePort();
      sendPort.send(receivePort.sendPort);
    
      receivePort.listen((msg) {
        print('Received in isolate: [$msg]');
        sendPort.send('ECHO: $msg');
      });
    
    }
    
    void main() {
      SendPort sendPort;
    
      ReceivePort receive = new ReceivePort();
      receive.listen((msg) {
        if (sendPort == null) {
          sendPort = msg;
        } else {
          print('From isolate: $msg');
        }
      });
    
      int counter = 0;
    
      Isolate.spawn(doStuff, receive.sendPort).then((isolate) {
        new Timer.periodic(const Duration(seconds:1), (t) {
          sendPort.send('Count is ${counter++}');
        });
      });
    }
    

    【讨论】:

      【解决方案3】:

      警告:此代码仅适用于非常旧的 Dart 版本。它不适用于 Dart 1.0 或更高版本。

      正如您提到将消息发布到隔离区,您需要处理它的发送端口。

      #import('dart:isolate');
      
      main() {
        SendPort sendPort = spawnFunction(doWork);
        sendPort.call("hey 1").then((String res) => print("result was: [$res]"));
        sendPort.call("hey 2").then((String res) => print("result was: [$res]"));
      }
      
      doWork() {
        port.receive((msg, reply) {
          msg = "msg $msg";
          reply.send(msg);
        });
      }
      

      然而,由于 Dart 主线程本身就是一个隔离,您可以使用全局 port 函数向其发送数据:

      #import('dart:isolate');
      #import('dart:io');
      
      main() {
         port.receive((data, reply) {
             // in here you can access objects created in the main thread
             print("handle [${data['text']}] for index ${data['index']}");
         });
      
         SendPort workPort = spawnFunction(doWork);
         workPort.send("msg", port.toSendPort());
      }
      
      doWork() {
         port.receive((msg, reply) {
            int i = 0;
            new Timer.repeating(1000, (Timer timer) {
               i++;
               var data = {
                  "text": "$msg $i",
                  "index": i
               };
               print("sending $data");
               reply.send(data);
            });
         });
      }
      

      请注意,对于可以在隔离之间来回发送的内容存在某些限制,并且目前隔离在 JS 和 VM 中的行为不同。当前的限制已经很好地描述了here

      【讨论】:

      • 但是如果我想让我的隔离生成/更新数据怎么办?就像一个游戏引擎。它应该运行所有计算,然后传递游戏中对象的更新状态。有没有一种有效的机制,还是我必须在 Isolates 之上构建它?
      • @Pius 您不能将引用发送到隔离区,所有处理过的数据都按照此处所述复制api.dartlang.org/dart_isolate/SendPort.html#send
      • 我根本不是在谈论引用。我说的是数据。你曾经在 JavaScript 中使用 Web Workers 工作吗?您可以随时从工作人员发送数据。尽可能多,如你所愿。 Worker 甚至可以在不监听主线程的情况下工作和发送数据,而主线程可以简单地使用回调函数接收数据。我说的是在隔离中等效的 self.postMessage 函数。
      • 噢……现在我明白了。非常感谢您。正是我需要的!
      • 计时器现在在 dart:isolate 中,而不是在 dart:io 中。
      【解决方案4】:

      您现在可以使用MessageBox 类以相反的方式进行通信。此代码在收到 MessageBox 的 Sink 端后立即从 Isolate 代码发送一条消息。主线程接收 Isolate 发送的消息,并在 Dartium 的控制台上打印出来。收到接收器后,您可以启动游戏逻辑并使用接收到的接收器对象发送更新。

      import 'dart:html';
      import 'dart:isolate';
      
      void main() {
        IsolateSink isolateSink = streamSpawnFunction(myIsolateEntryPoint);
        MessageBox isolateMessageBox = new MessageBox();
        isolateSink.add(isolateMessageBox.sink);
        isolateMessageBox.stream.listen((String data) {
          print(data);
        });
      }
      
      void myIsolateEntryPoint() {
        stream.listen((IsolateSink messageBoxSink) {
          messageBoxSink.add("Test");
        });
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-28
        • 2014-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多