1

ユーザーがキャンバス要素をクリックしたことに応じて、かなりコストのかかる操作を開始したいと考えています。

mouseDown(MouseEvent e) {
  print("entering event handler");
  var future = new Future<int>(expensiveFunction);
  future.then((int value) => redrawCanvas(value);
  print("done event handler");
}

expensiveFunction() {
  for(int i = 0; i < 1000000000; i++){
    //do something insane here
  }
}

redrawCanvas(int value) {
  //do stuff here
  print("redrawing canvas");
}

M4 Dart についての私の理解では、この将来のコンストラクターは "expensiveFunction" を非同期的に、つまりメイン スレッドとは別のスレッドで起動する必要があるということです。そして、「完了イベントハンドラー」がIDEの出力ウィンドウにすぐに出力され、しばらくしてから「キャンバスの再描画」が出力されるため、このように表示されます。ただし、要素をもう一度クリックすると、前のクリックから「expensiveFunction」の実行が完了するまで何も起こりません。

最初のフューチャがまだ完了していない場合でも、複数のクリックに応答して複数のフューチャをキューに入れることができるように、フューチャを使用して新しいスレッドで計算集約型関数を単純に起動するにはどうすればよいですか?

ありがとう。

4

2 に答える 2

3

別の回答で述べたように、先物は単なる「将来利用可能になる値のプレースホルダー」です。それらは必ずしも並行性を意味するわけではありません。

Dart には、並行性のための分離の概念があります。分離を生成して、並列スレッドまたはプロセスでコードを実行できます。

dart2js は、Isolate を Web Worker にコンパイルできます。Web Worker は別のスレッドで実行できます。

次のようなことを試してください:

import 'dart:isolate';

expensiveOperation(SendPort replyTo) {
  var result = doExpensiveThing(msg);
  replyTo.send(result);
}

main() async {
  var receive = new ReceivePort();
  var isolate = await Isolate.spawn(expensiveOperation, receive.sendPort);
  var result = await receive.first;
  print(result);
}

(私は上記をテストしていませんが、そのようなものはうまくいくはずです。)

于 2013-04-20T04:53:24.230 に答える
1

イベント ループとイベント キュー

Future はスレッドではないことに注意してください。それらは同時に実行されません。実際、Dart はシングルスレッドです。すべての Dart コードはイベント ループで実行されます。

イベント ループは、現在のDart アイソレートが生きている限り実行されるループです。Dart アプリケーションを開始するために呼び出すmain()と、isolate が作成され、main メソッドが完了し、イベント キューのすべてのアイテムも完了すると、それはアクティブではなくなります。

イベント キューは、実行を完了する必要があるすべての関数のセットです。Dart はシングル スレッドであるため、これらの関数はすべて一度に 1 つずつ実行する必要があります。したがって、イベント キュー内の 1 つの項目が完了すると、別の項目が開始されます。イベント キューの正確なタイミングとスケジューリングは、私が説明できるよりもはるかに複雑です。

したがって、非同期処理は、実行時間の長い実行によって単一のスレッドがブロックされるのを防ぐために重要です。UI では、プロセスに時間がかかると視覚的なジャンクが発生し、アプリの動作が妨げられる可能性があります。

先物

先物は、将来のある時点で利用可能になる値を表すため、その名前が付けられています。Future が作成されると、すぐに返され、実行が続行されます。

その Future (あなたの場合はexpensiveFunction) に関連付けられたコールバックは、イベント キューに追加されることによって「開始」されます。現在の分離から戻ると、コールバックが実行され、できるだけ早くthen.

ストリーム

Future は定義上非同期であり、いつ戻るかわからないため、コールバックをキューに入れ、順序を維持する必要があります。

Streamは、サブスクライブできるイベントを発行するオブジェクトです。あなたが書くとき、あなたは のストリームをcanvasElement.onClick.listen(...)求めています。それを でサブスクライブします。onClickMouseEventslisten

Streams を使用してイベントをキューに入れ、それらのイベントにコールバックを登録して、必要なコードを実行できます。

何を書くか

main() {
  // Used to add events to a stream.
  var controller = new StreamController<Future>();

  // Pause when we get an event so that we take one value at a time.
  var subscription = controller.stream.listen(
      (_) => subscription.pause());

  var canvas = new CanvasElement();
  canvas.onClick.listen((MouseEvent e) {
    print("entering event handler");
    var future = new Future<int>(expensiveFunction);

    // Resume subscription after our callback is called.
    controller.add(future.then(redrawCanvas).then(subscription.resume()));
    print("done event handler");
  });
}

expensiveFunction() {
  for(int i = 0; i < 1000000000; i++){
    //do something insane here
  }
}

redrawCanvas(int value) {
  //do stuff here
  print("redrawing canvas");
}

ここではredrawCanvas、マウスをクリックするたびに一時停止し、呼び出し後に再開することで、コールバックをキューに入れredrawCanvasています。

詳しくは

同様の質問に対するこの素晴らしい回答も参照してください。

Dart の非同期性について読み始めるのに最適な場所は、dart:io ライブラリに関するこの記事の最初の部分と、dart:async ライブラリに関するこの記事の最初の部分です。

Futures の詳細については、Futures に関するこの記事を参照してください。

Streams の情報については、Streams への追加に関するこの記事とStreams の作成に関するこの記事を参照してください。

于 2013-04-20T07:37:50.650 に答える