キャンセルされた未来が実際に終了するのを待っています
-
29-09-2019 - |
質問
私は持っています SwingWorker
スレッドの中断をチェックしないコードを呼び出します。通話後 worker.cancel(true)
, 、 worker.get()
メソッドがスローします CancellationException
すぐに(想定されるように)。ただし、バックグラウンドタスクのコードがスレッドを中断することをチェックしないため、喜んで実行され続けます。
バックグラウンドタスクを待つ標準的な方法はありますか 実際に 終了?タスクが終了するまで、「キャンセル...」メッセージまたはソートとブロックの何かを表示したいと考えています。 (必要に応じて、必要に応じてワーカークラスのフラグでこれを常に達成できると確信しています。他のソリューションを探しています。)
解決
私はこれを少し遊んでいましたが、これが私が思いついたものです。私はaを使用しています 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) {
}
}
}
他のヒント
これを行う標準的な、または既製の方法に最も近いものは 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に終了したことを示す特別な値を押すためのペア。あなたの process
スイングワーカーのオーバーライドメソッドは、この特別な価値を取り上げ、「キャンセル...」メッセージを隠します。これの利点は、投票やタイマーが必要ないことです。例のコードは、それを示しています done
タスクがキャンセルされるとすぐに呼び出されますが、タスクがキャンセルされた場合でも、パブリッシュ/プロセスメソッドのペアが機能します。
Paul Blessingのソリューションに触発されて、クラスになるために少し改善しました。サブクラスで、望ましいファンシトン性を得ることができます。
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();
}
}