Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvokeAll timeout interrupt

Question:

How do I make a series of threads that have timed out with invokeAll stop execution.

Background:

I have a list of callables I want to wait to complete. However, if they don't complete within a set time (say a second) I want to cancel them. This seems easy enough with

executor.invokeAll(callables, 1000l, TimeUnit.MILLISECONDS);

So after a second it will time out just fine and I can go on with my day. However in the background the threads are still active and potentially executing some pieces of code. Essentially they are not interrupted which I do not want to happen - I want the threads to stop executing.

Is there any way to use the futures list returned to stop the threads after invokeAll has hit its timeout?

like image 257
DWB Avatar asked Oct 29 '22 02:10

DWB


2 Answers

ExecutorService was designed to support cancellation of tasks that respond to interruption. So, the proper way to do this is by making your Callable implementations notice that they've been interrupted.

If your task is purely computational, or if it loops frequently, calling a third-party library, this is easy. Somewhere in your task, you'll have a loop that looks something like:

while (!Thread.interrupted()) {
  /* Keep going */
}

Of course, you might test other task-specific conditions in your loop too, but you should be testing the interrupted state of the current thread.

Calling interrupted() will clear the interrupted status. Calling Thread.currentThread().isInterrupted() will preserve the interrupted status. For worker threads in an ExecutorService, keeping the interrupted status doesn't matter too much—the service is interrupting its own threads, and when its thread returns from your task, the service clears the status without looking at it. But, it's a good habit to adopt, so that your code will work well if it is incorporated in a task. To re-assert the interrupted status after catching an InterruptedException or detecting it with Thread.interrupted(), simply call Thread.currentThread().interrupt().

If the task is a long-running call to a third-party library that doesn't support interruption, or a blocking call to an uninterruptible I/O operation, your "task" is much more difficult. You may be out of luck altogether, or you might be able to find a kludge to abort the operation asynchronously.

For example, if you create a Socket, and pass it (or its streams) to your task, you could asynchronously close the socket. If invokeAll times out, the caller would need additional code to close the socket.

like image 171
erickson Avatar answered Nov 15 '22 00:11

erickson


You can't have it both ways.

The ExecutorService "framework" allows you to only think in terms of Callable objects and Future results. This abstraction hides all the low level details of the threads involved. Thus: even if it would be possible, your idea would most likely be a "dirty hack".

In other words: if you want low-level, direct access to the threads doing something - then you might have to use your own "thread pooling" implementation.

Middle ground: keep in mind that these threads execute your Callable/Runnable instances. You could of course think up something where those objects regularly check some sort of "command queue". And when the queue contains a cancel request - then your code in the Callable stops doing its work.

like image 39
GhostCat Avatar answered Nov 15 '22 00:11

GhostCat