2009-06-17 15 views
1

スレッドプールサイズが1のThreadPoolExecutorを使用して、スイングワーカーを順番に実行しています。イベントが到着し、クライアントとサーバー間の通信を行うスイングワーカーを作成し、そのあとでui(done()メソッド内)を更新する特殊なケースがあります。ThreadPoolExecutorでのSwingWorkerのキャンセル

たときに、ユーザー火災(アイテムをクリックする)いくつかのイベントではなく、それらの多くが発生した場合、これが正常に動作します。しかし、これは私が現在実行中のすべてのスケジュールされた労働者を取り消す必要があるので起こります。問題は、ThreadPoolExecutorをサポートしているキューがSwingWorkerのキャンセル処理を認識していないことです(少なくともそのように思われます)。スケジュールされた労働者はキャンセルされますが、すでに稼働している労働者は就労しませ

したがって、キャンセルされていない限りすべてのワーカーの参照を保持するタイプ<T extends SwingWorker>の並行キューを追加しました。新しいイベントが到着したときにキュー内のすべてのSwingWorkersで.cancel(true)が呼び出され、 SwingWorkerをThreadPoolExecutorに追加します。

概要:SwingWorkersが作成され、単一のスレッドでThreadPoolExecutorで実行されます。最後に送信されたワーカーのみが実行されている必要があります。

は、この問題を解決するためのいずれかの選択肢があります、またはそれはこのようにそれを行うには「OK」とは?興味があるだけ

...

+0

動作しますか?それが壊れていない場合は、それを修正しないでください(またはそれまで笑ってください) – Gandalf

+0

はい、それは動作します。しかし、並行コードの作成は難しいです。自分の仕事のコンセプトが存在するかどうかを知りたいだけです。 – MrWhite

答えて

2

最後に受信したRunnableを実行するだけの単一スレッドThreadPoolExecutorを作成する1つの方法は、適切なキュークラスをサブクラス化し、すべての追加メソッドをオーバーライドして新しい実行可能ファイルを追加する前にキューをクリアすることです。次に、そのキューをThreadPoolExecutorの作業キューとして設定します。

+0

ニースとして実行します。それは、取り消しを必要とするタスクの別のリストを保持する私の考えよりはるかにクリーンです。 –

+0

これは私の実装とほぼ同じです。 – MrWhite

0

は、なぜあなたは仕事のこの種を行うにはThreadPoolExecutorが必要なのですか?

あなたのSwingWorkersのソースはいくつありますか?ソースが1つの場合は、別の方法を使用する必要があります。

たとえば、ある種類の作業スレッドを処理するクラスを定義できます。このクラスは、ユーザーがそのクラス内でアクションを起こして気にすることができる1種類のアイテムにリンクされています。

+0

一度に1つのタスクしか実行されないようにするには、スレッドプールエグゼキュータを1つのスレッドで実行する必要があります。しかし、それらの多くが発生することはありませんので、私はそれらをキューに入れなければなりません。 – MrWhite

0

SwingWorkerを使用する代わりに、ThreadPoolExecutorを使用してクライアントとサーバー間の通信を行い、その後SwingUtilities.invokeLaterを呼び出してUIを更新することはできませんでした。結果?これは私にとっては少しきれいで、イベントやUIの更新が確実に処理されるようにしてくれます。

あなたのエグゼキュータにタスクを送信すると、必要に応じてタスクを取り消すことができるように、あなたは彼らの将来のインスタンスへの参照を保つことができます。

+0

問題は、実行者から取得できる将来は、実行可能なインターフェイスをラップするだけなので、スイングワーカーのような取り消しの可能性を提供しないということです。 – MrWhite

+0

しかし、どちらも同じキャンセル方法を提供しています。 SwingWorkerは、Futureインタフェースを実装しています。 – Mark

+0

私が言ったように。それはエグゼクティブがそれを認識していないようです。ちょうどRunnable – MrWhite

0

問題が正しく理解できるかどうか確認してください。タスクのFIFOキューがあり、その中で最も古いものだけが実行されています。各タスクは、完了したらUIを更新する必要があります。しかし、特定のユーザーイベントが入った場合、すべてのタスクをキャンセルする必要があります。つまり、実行中のタスクをキャンセルする必要があります。実行していないタスクをキューから削除する必要があります。そうですか?それはあなたが唯一のワーカースレッドではなく、タスクごとに1つずつ必要があるので、私はSwingWorkerを使用していないだろう、と仮定すると

FutureTaskで十分であるはずです(SwingUtilities.invokeLater()に必要な呼び出しを行い、UIを更新するにはdone()を上書きすると仮定します)。

FutureTaskをキャンセルすると、そのrun()メソッドが呼び出されても、何も行われません。したがって、FutureTaskExecutorServiceに安全に提出することができます。これは、エグゼキュータが実行しようとしても取り消しが機能することを知っています。

キャンセルする必要がある可能性のあるすべてのFutureTasksのリストを保持し、ユーザーイベントが到着したときにそれらをすべてキャンセルすることが十分な解決策であると思われます。ExecutorServiceはまだ実行しようとしますが、基本的にはノーオペレーションです。完了したタスクがリストから削除されていることを確認する必要があります。リストが更新され、スレッドセーフな方法で(おそらくExecutorServiceにタスクを置く同じスレッドから)更新されていることを確認する必要があります。あまりにも難しい。

私はちょうど1時間で下のコードを流しました。私はそれが正しいと確信しませんでしたが、あなたはそのアイデアを得ます。 :)

/** Untested code! Use at own risk. */ 
public class SwingTaskExecutor { 

    // //////////////////////////////////////////////////////////// 
    // Fields 

    private final ExecutorService execSvc = Executors.newFixedThreadPool(1); 

    private final Lock listLock = new ReentrantLock(); 
    private final List<ManagedSwingTask<?>> activeTasks = 
      new ArrayList<ManagedSwingTask<?>>(); 

    // //////////////////////////////////////////////////////////// 
    // Public methods 

    public <T> Future<T> submit(SwingTask<T> task) { 
     ManagedSwingTask<T> managedTask = new ManagedSwingTask<T>(task); 
     addToActiveTasks(managedTask); 
     execSvc.submit(managedTask); 
     return managedTask; 
    } 

    public void cancelAllTasks() { 
     listLock.lock(); 
     try { 
      for (ManagedSwingTask<?> t: activeTasks) { 
       t.cancel(true); 
      } 
      activeTasks.clear(); 
     } finally { 
      listLock.unlock(); 
     } 
    } 

    // //////////////////////////////////////////////////////////// 
    // Private methods 

    private <T> void addToActiveTasks(ManagedSwingTask<T> managedTask) { 
     listLock.lock(); 
     try { 
      activeTasks.add(managedTask); 
     } finally { 
      listLock.unlock(); 
     } 
    } 

    // //////////////////////////////////////////////////////////// 
    // Helper classes 

    private class ManagedSwingTask<T> extends FutureTask<T> { 

     private final SwingTask<T> task; 

     ManagedSwingTask(SwingTask<T> task) { 
      super(task); 
      this.task = task; 
     } 

     @Override 
     public void cancel(boolean mayInterruptIfRunning) { 
      try { 
       task.cancel(); 
      } finally { 
       super.cancel(mayInterruptIfRunning); 
      } 
     } 

     @Override 
     protected void done() { 
      removeFromActiveTasks(); 
      updateUIIfDone(); 
     } 

     private void removeFromActiveTasks() { 
      listLock.lock(); 
      try { 
       activeTasks.remove(this); 
      } finally { 
       listLock.unlock(); 
      } 
     } 

     private void updateUIIfDone() { 
      if (isDone()) { 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         task.updateUI(); 
        } 
       }); 
      } 
     } 
    } 

    public static interface SwingTask<T> extends Callable<T> { 

     /** Called from the EDT if task completes successfully */ 
     void updateUI(); 

     /** Hook in case there's task-specific cancellation to be done*/ 
     void cancel(); 
    } 
} 

何か、とにかく。

二重確実にしたい場合は、ExecutorServiceをシャットダウンして交換することができますが、これはおそらく必要ありません。

関連する問題