繁体   English   中英

Future.cancel(true)不能可靠地取消/中断线程

[英]Future.cancel(true) does not reliably cancel/interrupt thread

我尝试与CompletionService并行执行几个任务。 当我尝试执行取消时,就会出现问题。

这是我使用的代码的草图:

void startTasks(int numberOfTasks) throws Exception {

    ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
    CompletionService<TaskResultType> completionService = new ExecutorCompletionService<TaskResultType>(executor);
    ConcurrentLinkedQueue<TaskResultType> results = new ConcurrentLinkedQueue<BenchmarkResult>();
    ArrayList<Future> futures = new ArrayList<Future>();

    for (int i = 0; i < numberOfTasks ; i++) {
        TypeOfTask task = ... ; 
        Future future = completionService.submit(task);
        futures.add(future);
    }

    boolean failed = false;
    Throwable cause = null;
    for (int i = 0; i < numberOfThreads; i++) {
        try {
            Future<TaskResultType> resultFuture = completionService.take();
            TaskResultType result = resultFuture.get();
            results.add(result);
        } catch (ExecutionException e) {
            failed = true;
            cause = e.getCause();
            /* cancel all other running tasks in case of failure in one task */
            for (Future future : futures) {
                future.cancel(true);
            }
        } catch (CancellationException e) {
            // consume (planned cancellation from calling future.cancel())
        }

    }
    executor.shutdown();
    // code to throw an exception using cause
}

任务实现Callable

现在,在大多数情况下,当我在其中一个任务中抛出异常时,它可以正常工作,即,我立即从其他任务中获得CancellationExceptions,并且任务立即完成(将这种情况称为A)。 但有时(让我们将这种情况称为B),某些任务首先完成,然后引发CancellationException。 对于所有任务,Future.cancel(true)在两种情况下均返回true(具有初始ExecutionException的任务除外,因为该任务已被取消)。

我使用Thread.currentThread.isInterrupted()检查中断标志,在完成的任务中(即取消失败的任务),将中断标志设置为false。

在我看来,所有这些似乎都是非常奇怪的行为。 有人知道问题可能是什么吗?

更新资料

到目前为止,我最好的想法是,在包含任务的代码中的某个深处(只有一些高级代码来自我自己),中断状态被消耗了,例如被捕获的InterruptedException所占用,该异常未将Thread.interrupt()调用为重新建立状态。 由于线程的调度,Future.cancel()设置中断标志的确切时间可能会略有不同,这可以解释不一致的行为。 消耗中断状态会解释情况B的行为吗?

但有时,某些任务会先完成,然后引发CancellationException。 对于所有任务,Future.cancel(true)在两种情况下均返回true(具有初始ExecutionException的任务除外,因为该任务已被取消)。

如果您的程序未完成(或者引发异常后需要很长时间才能完成),那么我怀疑您的问题是任务之一正在做IO或以其他方式被阻塞,并且没有检查Thread.currentThread().isInterrupted() 因此,即使您取消了Future并且线程被中断,也不会被检测到。

但是,程序似乎正在完成。 所以我不确定这里是什么错误情况。 如果捕获到异常,请在列表中的所有期货上调用future.cancel(true) 投掷的物件和已经完成的物件均应返回false因为它们无法取消。 从cancel返回true的那些应该已经被中断。

例如。 如果倒数第二个线程引发异常,则future.cancel(true)应该仅对最后一个正在运行的线程返回true

要做的一件事是在期货到期时将其删除,这样您就不必重新取消已完成的工作。 但是可能要做的就是掩盖您现在所看到的问题:

Future<TaskResultType> resultFuture = completionService.take();
futures.remove(resultFuture);

更新:

某些代码很可能吞下该中断。 不幸的是,它一直在发生。 如果某些线程在取消时并没有立即完成并正在运行以完成,则可能是这种情况。

但有时,某些任务会先完成,然后引发CancellationException。

可能是您取消了一个任务,该任务通常被中断了(在这种状态下,您可能认为它返回了一个结果,但是对于CompletionService,它被取消了),future是由take()返回的,您调用future.get() ,还有CancellationException

您还可以查看Guava的Futures.allAsList ,它似乎在做类似的事情:

创建一个新的ListenableFuture,其值是一个列表,其中包含所有输入期货的值(如果全部成功)。 如果任何输入失败,则返回的future失败。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM