3

私は現在、FutureTasksとExecutorsを使用して、マルチスレッド環境で厄介なバグを探しています。基本的な考え方は、固定数のスレッドに、テーブルに表示される結果を計算する個々のFutureTasksを実行させることです(ここではGUIの側面を気にしないでください)。

私はこれを長い間見てきました、私は自分の正気を疑うようになり始めています。

このコードを考えてみましょう。

public class MyTask extends FutureTask<Result> {
   private String cellId;
   ...
   protected void done() {
      if (isCancelled()) return;
      try {
          Result r = get(); // should not wait, because we are done
          ... // some processing with r
          sendMessage(cellId, r);
      } catch (ExecutionException e) { // thrown from get
         ... 
      } catch (InterruptedException e) { // thrown from get
         ... 
      }
   }
   ...
}

MyTaskdone()のインスタンスを処理するエグゼキュータによって呼び出された場合、タスクがキャンセルされたため、そこに到達したかどうかを確認します。もしそうなら、私は残りのすべての活動をスキップします、特に私は電話しませんsendMessage()

FutureTask.done()のドキュメントには次のように書かれています。

このタスクが状態isDoneに遷移するときに呼び出される保護されたメソッド(通常またはキャンセルを介して)。デフォルトの実装は何もしません。サブクラスは、このメソッドをオーバーライドして、完了コールバックを呼び出したり、簿記を実行したりできます。このメソッドの実装内でステータスを照会して、このタスクがキャンセルされたかどうかを判断できることに注意してください。(APIリファレンス

しかし、のドキュメントから得られないのは、実行FutureTaskのセマンティクスです。最初にチェックに合格したが、その直後に他のスレッドが私のメソッドを呼び出した場合はどうなりますか?それは私の仕事の考えを変え、それ以降に返信する原因になりますか? done()isCancelled()cancel()isCancelled() == true

もしそうなら、メッセージが送信されたかどうかを後でどうやって知ることができますか?見てみるisDone()と、タスクの実行が終了したisCancelled()ことがわかりますが、そのときもそうであったように、メッセージを時間内に送信できるかどうかはわかりませんでした。

たぶんこれは明らかですが、私は今それを実際には見ていません。

4

4 に答える 4

4

FutureTask#done()は、特定のインスタンスに対して1回だけ呼び出され、1つの理由でのみ呼び出されrun()ます。エラーの有無にかかわらず完了したかcancel()、前述のイベントのいずれかが発生する前に実行されました。これらの結果のいずれかによる完了の記録はラッチングです。FutureTask競合するイベントが「同時に」発生しているように見えても、完了した理由を変更することはできません。

したがって、またはFutureTask#done()の1つだけの範囲内で、それ以降はtrueを返します。エラーによる報告と正常な完了による報告を区別することは困難です。値の成功した降伏を記録する試みまたは例外の発生を記録する試みが固執するかどうかを決定するために両方が内部に委任するため、オーバーライドまたは決定的にすることはできません。どちらかのメソッドをオーバーライドすると、それが呼び出されたことがわかりますが、基本実装によって行われた決定を観察することはできません。いずれかのイベントが「遅すぎる」場合(たとえば、キャンセル)、値または例外を記録する試みは無視されます。isCancelled()isDone()isDone()set()setException(Throwable)AQS

実装を研究して、キャンセルされていない成功した結果をエラーから識別するために私が見る唯一の方法は、弾丸を噛んで呼び出すことget()です。

于 2009-11-22T01:08:24.613 に答える
3

APIから(強調鉱山):

public boolean cancel(boolean mayInterruptIfRunning)

インターフェイスからコピーされた説明:Future

このタスクの実行をキャンセルしようとします。タスクがすでに完了している、キャンセルされている、またはその他の理由でキャンセルできなかった場合、この試行は失敗します。

そのため、FutureTaskは、タスクがisDoneステージに移行したときにタスクをキャンセルできないという前提に基づいて機能しています。

于 2009-08-05T18:51:31.760 に答える
3

ExecutorServiceによって返されたFuture<V>オブジェクトの結果に基づいて、タスクの「外部」にメッセージを送信してみませんか?私はこのパターンを使用しましたが、うまく機能しているようです。ExecutorServiceを介して一連のCallable <V>タスクを送信します。次に、プライマリタスクごとに、プライマリタスクのFuture <V>を待機し、Future <V>プライマリタスクが正常に完了したことを示した場合にのみ、何らかのフォローアップアクション(メッセージの送信など)を実行するセカンダリタスクを送信します。 。このアプローチには当て推量はありません。Future <V> .get()の呼び出しが戻ると、タスクが終了状態に到達したことが保証されます。タイムアウト引数を取るget 。

このアプローチを採用する場合は、2つの別々のExecutorServiceインスタンスを使用する必要があります。1つはプライマリタスク用で、もう1つはセカンダリタスク用です。これは、デッドロックを防ぐためです。スレッドプールのサイズが制限されている場合に、セカンダリタスクを起動して、プライマリタスクの起動をブロックする可能性はありません。

FutureTask<V>を拡張する必要はまったくありません。タスクをCallable<V>オブジェクトとして実装するだけです。ただし、何らかの理由でCallable <V>コード内からタスクがキャンセルされたかどうかを検出したい場合は、 Thread.interrupted()を使用してスレッドの割り込みステータスを確認してください。

于 2009-08-05T21:44:35.360 に答える
-1

インスタンスがハングアップしているときに呼び出しcancel()て、何が起こるかを確認できる小さなテストケースを作成することをお勧めします。Futuredone()

于 2009-08-05T20:51:45.200 に答える