2010-08-19 51 views
6

我有一個SwingWorker它調用一些代碼,不檢查線程中斷。撥打worker.cancel(true)後,worker.get()方法將立即拋出CancellationException(因爲它應該)。但是,由於後臺任務的代碼從不檢查其線程是否被中斷,因此它繼續執行。等待取消的將來實際完成

是否有一個標準的方式來等待後臺任務到實際上完成?我正在尋找顯示「取消...」的消息或類似的東西,並阻止任務終止。 (如果有必要,我肯定我可以在工人階級中用旗幟完成這項工作,只是尋找其他解決方案。)

回答

3

我玩過這一點,這是我想出來的。我正在使用CountDownLatch,並且基本上將其await()方法作爲我的SwingWorker對象上的方法公開。仍在尋找更好的解決方案。

final class Worker extends SwingWorker<Void, Void> { 

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1); 

    @Override 
    protected Void doInBackground() throws Exception { 
     try { 
      System.out.println("Long Task Started"); 

      /* Simulate long running method */ 
      for (int i = 0; i < 1000000000; i++) { 
       double d = Math.sqrt(i); 
      } 

      return null; 
     } finally { 
      actuallyFinishedLatch.countDown(); 
     } 
    } 

    public void awaitActualCompletion() throws InterruptedException { 
     actuallyFinishedLatch.await(); 
    } 

    public static void main(String[] args) { 
     Worker worker = new Worker(); 
     worker.execute(); 

     try { 
      TimeUnit.SECONDS.sleep(1); 
     } catch (InterruptedException e) { 

     } 

     System.out.println("Cancelling"); 
     worker.cancel(true); 

     try { 
      worker.get(); 
     } catch (CancellationException e) { 
      System.out.println("CancellationException properly thrown"); 
     } catch (InterruptedException e) { 

     } catch (ExecutionException e) { 

     } 

     System.out.println("Awaiting Actual Completion"); 
     try { 
      worker.awaitActualCompletion(); 
      System.out.println("Done"); 
     } catch (InterruptedException e) { 

     } 
    } 

} 
1

最接近標準或現成的方法是progress財產和/或由SwingWorker提供的發佈/處理方法對。您可以在方法末尾將此值設置爲「我已完成」值,以指示後臺工作已完成。等待揮杆員的線程可以發出「正在取消...」消息,並定期檢查進度,看看是否已完成。如果等待線程是擺動式EDT,那麼您需要使用定時器定期檢查進度屬性,並在完成時清除取消消息。

下面是一個運行頑固後臺線程,這是取消了一些示例代碼,然後等待,直到進度達到100

@Test 
public void testSwingWorker() 
{ 
    SwingWorker worker = new SwingWorker() { 

     @Override 
     protected void process(List chunks) 
     { 
      for (Object chunk : chunks) 
      { 
       System.out.println("process: "+chunk.toString()); 
      } 
     } 

     @Override 
     protected void done() 
     { 
      System.out.println("done"); 
     } 

     @Override 
     protected Object doInBackground() throws Exception 
     { 
      // simulate long running method 
      for (int i=0; i<1000000000; i++) 
      { 
       double d = Math.sqrt(i); 
      } 
      System.err.println("finished"); 
      publish("finished"); 
      setProgress(100); 
      return null; 
     } 
    }; 
    Thread t = new Thread(worker); 
    t.start(); 

    try 
    { 
     worker.get(1, TimeUnit.SECONDS); 
    } 
    catch (InterruptedException e)  { 
    } 
    catch (ExecutionException e)  { 
    } 
    catch (TimeoutException e)  { 
    } 

    worker.cancel(true); 

    // now wait for finish. 
    int progress = 0; 
    do 
    { 
     try 
     { 
      Thread.sleep(1000); 
     } 
     catch (InterruptedException e) 
     { 
     } 
     progress = worker.getProgress(); 
     System.out.println(String.format("progress %d", progress)); 
    } 
    while (progress<100); 
} 

的另一種方法是使用publish\process方法對推一個特殊值表明後臺線程已完成到EDT。 SwingWorker中的你的process重寫方法然後拾取這個特殊值並隱藏「正在取消...」消息。這樣做的好處是不需要輪詢或定時器。示例代碼顯示,雖然done在任務取消後立即被調用,但即使任務被取消,發佈/進程方法對仍然可以工作。

+0

這兩項想法是事情我還沒有考慮。我不喜歡以你引用的相同理由來使用進度 - 它需要投票。我大多喜歡發佈/過程的想法,但它將我將要完成的任務完成的邏輯與「SwingWorker」類本身結合在一起。出於同樣的原因,我很少使用'done()'方法,而是通常將監聽器添加到worker對象中。 – 2010-08-19 17:20:32

+0

我同意你的看法,發佈/過程是兩者中較好的一個。至於耦合,你可以通過創建一個重寫流程方法的SwingWorker子類來解耦它,監聽「我完成了」的值,並通知註冊的監聽者後臺任務真的完成了。這將邏輯與擺動工作人員分開。 – mdma 2010-08-19 19:20:52

+0

的確如此,但是在此期間監聽對象將如何阻塞直到它收到通知? – 2010-08-20 12:59:08

1

由保羅祝福的解決方案,我改進了一點,成爲一類,你也可以繼承,以獲得所需funcitonality啓發:

class AwaitingWorker<T,V> extends SwingWorker<T, V> { 

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1); 

    /** 
    * Override this to do something useful 
    */ 
    protected abstract void performOperation(); 

    @Override 
    protected final T doInBackground() throws Exception { 
     try { 
      return performOperation(); 
     } finally { 
      actuallyFinishedLatch.countDown(); 
     } 
    } 

    public void awaitActualCompletion() throws InterruptedException { 
     actuallyFinishedLatch.await(); 
    } 

} 
+0

我假設done()現在調用awaitActualCompletion(),但現在done()可以是一個長時間運行的任務,因爲它在用cancel()取消之後等待doInBackground()完成。它會阻止EventDispatchThread – 2012-07-20 15:14:39

+0

是不是可以取消預定執行但尚未啓動的任務?看起來像awaitActualCompletion需要以某種方式檢查任務是否在調用await之前開始之前未被取消。 – Ryan 2014-09-18 21:54:09