Entra

View Full Version : [JAVA][Concurrency API]Supportare la cancellazione di un task


banryu79
05-03-2010, 13:37
Salve,
ho la neccessità di definire dei task che vengono eseguiti in parallelo tramite una fixed thread pool:

...
ExecutorService fixedPool = Executors.newFixedTrheadPool(N_THREADS);


Questi task sono definiti come implementazioni di Callable:

private class SetupTASK implements Callable<OpResult<?>>
{
private final Session SESSION;

SetupTASK(Session s) {
SESSION = s;
}

@Override
public OpResult<?> call() throws Exception {
...
}
}


Creati e passati alla threadPool perchè li esegua in modo asincrono:

private void setupSessions() {
FutureTask<?>[] taskBattery
= new FutureTask<?>[POOL.N_THREADS];

// create tasks "battery"
for (int i=0; i<POOL.N_THREADS; i++) {
SetupTASK t = new SetupTASK(SESSIONS);
taskBattery[i] = new FutureTask<OpResult<?>>(t);
}

// execute all tasks
POOL.submitTasks(taskBattery);

// wait until "battery" execution completes
OpResult<?> res = null;
for (int i=0; i<POOL.N_THREADS; i++) {
try {
res = (OpResult<?>) taskBattery[i].get();
... do something with 'res'
}
catch(ExecutionException ignored1) {}
catch(InterruptedException ignored2) {}
}
}

...

// on another scope, definition of POOL.submitTasks method:
<E> void submitTasks(FutureTask<E>[] tasks)
{
for (FutureTask<E> t : tasks)
fixedPool.submit(t);
}


Dato che devo rendere la batteria di task in esecuzione [taskBattery] "cancellabile" (l'utente vede una progress bar e un bottone "Annulla") pensavo di chiamare il metodo cancel() esposto da ogni FutureTask in taskBattery, con il parametro mayInterruptIfRunning valorizzato a true:

cancel

boolean cancel(boolean mayInterruptIfRunning)

Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true.

Parameters:
[I]mayInterruptIfRunning - true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete
Returns:
false if the task could not be cancelled, typically because it has already completed normally; true otherwise


I miei dubbi sono:

In questo caso, perchè la cosa funzioni, devo supportare l'interruzione del thread che esegue il task?

Se sì, è corretto supportarlo chiamando in momenti opportuni Thread.interrupted() sul thread che sta eseguendo il task e se si verifica l'interrupt lanciando una InterruptedException? [esempio di implementazione del metodo call() di un task-tipo]:

@Override
public OpResult<?> call() throws Exception {
OpResult<?> res = new OpResult<Object>();

checkInterrupt();
res = SESSION.connect(BASE_SESSION);
if (res.isError()) {
return res;
}

checkInterrupt();
res = SESSION.login(BASE_SESSION);
if (res.isError()) {
return res;
}

String[] dirs = BASE_SESSION.arrayPath();
for (String dir : dirs) {
checkInterrupt();
res = SESSION.changeDir(dir);
if (res.isError()) {
return res; // break loop
}
}

return res;
}

private void checkInterrupt() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
}

Esistono delle alternative?
Grazie mille, anche solo per la pazienza di aver letto :)

P.S.: per via di scelte dell'architettura del progetto SwingWorker non è un'opzione.

PGI-Bis
05-03-2010, 15:31
In sintesi sì. Questo:

Callable<Void> task = new Callable<Void>() {

public Void call() throws Exception {
while(true) {}
}
}

E' un compito che non può essere interrotto mentre questo:

Callable<Void> task = new Callable<Void>() {

public Void call() throws Exception {
while(true) {
if(Thread.interrupted()) throw new Exception("Lavalachelavaben");
}
}
}

E' terminabile.

banryu79
05-03-2010, 17:18
I miei dubbi erano dati dal fatto che FutureTask implementa un metodo 'cancel' e un metodo 'isCancelled'.

Ricordando gli esempi concernenti l'uso del metodo 'doInBackground' di SwingWorker avevo il dubbio fosse sufficiente, anche per quanto riguarda il metodo 'run' di un Callable, controllare internamente con 'isCancelled' se il task fosse stato annullato da una precedente chiamata su 'cancel'.

Invece non c'entra niente: isCancelled viene chiamato sempre e solo alla fine della computazione del FutureTask e serve solo per sapere a posteriori se il task ha terminato la computazione perchè cancellato oppure no.

Grazie della conferma.