简体   繁体   English

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

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

I try to execute several tasks in parallel with a CompletionService. 我尝试与CompletionService并行执行几个任务。 The problems arise, when i try to implement cancelation. 当我尝试执行取消时,就会出现问题。

Here is a sketch of the code I use: 这是我使用的代码的草图:

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
}

The tasks implement Callable . 任务实现Callable

When I now throw an exception in one of the tasks in most of the cases it works out fine, ie I immediately get the CancellationExceptions from the other tasks and the tasks finish immediately (lets call this case A). 现在,在大多数情况下,当我在其中一个任务中抛出异常时,它可以正常工作,即,我立即从其他任务中获得CancellationExceptions,并且任务立即完成(将这种情况称为A)。 But sometimes (lets call this case B), some of the tasks finish first and then throw the CancellationException. 但有时(让我们将这种情况称为B),某些任务首先完成,然后引发CancellationException。 Future.cancel(true) returned true in both cases for all tasks (except the one with the initial ExecutionException, because this one was already canceled). 对于所有任务,Future.cancel(true)在两种情况下均返回true(具有初始ExecutionException的任务除外,因为该任务已被取消)。

I check for the interrupted flag with Thread.currentThread.isInterrupted(), in the tasks that do complete (ie the tasks where the cancelation is unsuccessful), the interrupted flag is set to false. 我使用Thread.currentThread.isInterrupted()检查中断标志,在完成的任务中(即取消失败的任务),将中断标志设置为false。

All that seems to be very strange behavior in my eyes. 在我看来,所有这些似乎都是非常奇怪的行为。 Anybody any idea what the problem could be? 有人知道问题可能是什么吗?

Update 更新资料

The best idea so far I have is, that somewhere deep within the code comprising the task (only some high level code is from myself) the interrupted status is consumed, eg by a catched InterruptedException which doesn't call Thread.interrupt() to reestablish the status. 到目前为止,我最好的想法是,在包含任务的代码中的某个深处(只有一些高级代码来自我自己),中断状态被消耗了,例如被捕获的InterruptedException所占用,该异常未将Thread.interrupt()调用为重新建立状态。 The exact time the interrupted flag is set by Future.cancel() might vary slightly due to scheduling of the threads, that would explain the inconsistent behavior. 由于线程的调度,Future.cancel()设置中断标志的确切时间可能会略有不同,这可以解释不一致的行为。 Would a consumed interrupted status explain the behavior of case B? 消耗中断状态会解释情况B的行为吗?

But sometimes, some of the tasks finish first and then throw the CancellationException. 但有时,某些任务会先完成,然后引发CancellationException。 Future.cancel(true) returned true in both cases for all tasks (except the one with the initial ExecutionException, because this one was already canceled). 对于所有任务,Future.cancel(true)在两种情况下均返回true(具有初始ExecutionException的任务除外,因为该任务已被取消)。

If your program was not finishing (or taking a long time to finish after an exception is thrown) then I suspect your problem is that one of the tasks is doing IO or otherwise blocked and is not checking for Thread.currentThread().isInterrupted() . 如果您的程序未完成(或者引发异常后需要很长时间才能完成),那么我怀疑您的问题是任务之一正在做IO或以其他方式被阻塞,并且没有检查Thread.currentThread().isInterrupted() So even though you have canceled the Future and the thread is interrupted, this doesn't get detected. 因此,即使您取消了Future并且线程被中断,也不会被检测到。

However, it seems like the program is finishing. 但是,程序似乎正在完成。 So I'm not sure what the error case is here. 所以我不确定这里是什么错误情况。 If you catch an exception you call future.cancel(true) on all of the futures in the list. 如果捕获到异常,请在列表中的所有期货上调用future.cancel(true) The one that threw and the ones that have already finished should all return false since they can't be canceled. 投掷的物件和已经完成的物件均应返回false因为它们无法取消。 The ones that returned true from cancel should have been interrupted. 从cancel返回true的那些应该已经被中断。

For example. 例如。 if the 2nd to last thread throws an exception then future.cancel(true) should only return true for the last thread running. 如果倒数第二个线程引发异常,则future.cancel(true)应该仅对最后一个正在运行的线程返回true

One thing to do is to remove the futures as they finish so you don't re-cancel already completed jobs. 要做的一件事是在期货到期时将其删除,这样您就不必重新取消已完成的工作。 But all that might do is mask the issue you are seeing now: 但是可能要做的就是掩盖您现在所看到的问题:

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

Update: 更新:

It is highly possible that some code is swallowing the interrupt. 某些代码很可能吞下该中断。 It happens all of the time unfortunately. 不幸的是,它一直在发生。 If some of the threads are not finishing immediately when they are canceled and are running to completion then this is probably what is happening. 如果某些线程在取消时并没有立即完成并正在运行以完成,则可能是这种情况。

But sometimes, some of the tasks finish first and then throw the CancellationException. 但有时,某些任务会先完成,然后引发CancellationException。

Could it be the case that you cancel a task, it's normally interrupted (in this state you may think that it returns a result, but for the CompletionService it's cancelled), the future is being returned by take() , you call future.get() and there is the CancellationException . 可能是您取消了一个任务,该任务通常被中断了(在这种状态下,您可能认为它返回了一个结果,但是对于CompletionService,它被取消了),future是由take()返回的,您调用future.get() ,还有CancellationException

You could also have a look at Guava's Futures.allAsList , which seems to be doing a very similar thing: 您还可以查看Guava的Futures.allAsList ,它似乎在做类似的事情:

Creates a new ListenableFuture whose value is a list containing the values of all its input futures, if all succeed. 创建一个新的ListenableFuture,其值是一个列表,其中包含所有输入期货的值(如果全部成功)。 If any input fails, the returned future fails. 如果任何输入失败,则返回的future失败。

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

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