2009-12-16 8 views
13

Ho un compito lungo in esecuzione, qualcosa come:Java lunga esecuzione compito Discussione interrompere vs annullare bandiera

public void myCancellableTask() { 
    while (someCondition) { 
     checkIfCancelRequested(); 
     doSomeWork(); 
    } 
} 

Il compito può essere cancellato (un annullamento viene richiesta e checkIfCancelRequested() controlla il flag annulla). Generalmente quando scrivo loop cancellabili come questo, uso un flag per indicare che è stata richiesta una cancellazione. Ma, so che potrei anche usare Thread.interrupt e controllare se il thread è stato interrotto. Non sono sicuro quale sarebbe l'approccio preferito e perché, pensieri?

grazie,

Jeff

risposta

6

Interrupt farà saltare il filo da un elenco di condizioni di attesa specificato. La tua bandiera di annullamento non lo farà. Se si desidera interrompere l'attesa su IO ed eventi, utilizzare l'interrupt. Altrimenti usa il tuo.

0

Penso che sia una questione di preferenza nella maggior parte dei casi. Personalmente vorrei andare per la bandiera fatta a mano. Ti dà più controllo - ad esempio, in questo modo ti assicuri che il tuo thread non lasci alcun altro oggetto in uno stato incoerente. Inoltre, se le prestazioni sono davvero critiche, tieni presente che l'utilizzo di eccezioni ha un sovraccarico (anche se è trascurabile nel 99% dei casi).

1

Dipende dall'implementazione doSomeWork(). È una pura computazione o (in qualsiasi momento) coinvolge chiamate di API di blocco (come IO)? Alla risposta di bmargulies, molte API di blocco in JDK sono interrompibili e propagano l'eccezione interrotta nello stack.

Quindi, se il lavoro comporta potenzialmente bloccare le attività, si need'll di prendere in considerazione gli interrupt anche se si decide di controllare il processo utilizzando una bandiera, e dovrebbe opportunamente catturare e gestire/propagare le interruzioni.

Oltre a ciò, se si fa affidamento su un flag, assicurarsi che il flag sia dichiarato con la semantica volatile.

20

Un problema con l'utilizzo di interrupt è che se non si controlla tutto il codice viene eseguito, si corre il rischio di interruzione non funziona "correttamente" a causa della qualcun altro comprensione rotto di come gestire gli interrupt nella loro biblioteca. Questa è l'API invisibile. esporta un'API attorno alla sua gestione di interrupt s da cui diventi dipendente.

Nel tuo esempio, supponiamo doSomeWork era in un JAR 3rd-party e si presenta come:

public void doSomeWork() { 
    try { 
     api.callAndWaitAnswer() ; 
    } 
    catch (InterruptedException e) { throw new AssertionError(); } 
} 

Ora si deve gestire un AssertionError (o qualsiasi altra cosa la biblioteca si sta utilizzando potrebbe gettare). Ho visto sviluppatori esperti lanciare ogni genere di assurdità alla ricezione di interruzioni! D'altra parte, forse il metodo si presentava così:

public void doSomeWork() { 
    while (true) { 
     try { 
      return api.callAndWaitAnswer() ; 
     } 
     catch (InterruptedException e) { /* retry! */ } 
    } 
} 

Questa "uso improprio" di interrupt fa sì che il programma per ciclo a tempo indeterminato. Ancora una volta, non respingere questo come ridicolo; là fuori ci sono molti meccanismi di gestione degli interrupt rotti.

Almeno l'utilizzo di una propria bandiera sarà completamente invisibile a qualsiasi libreria di terze parti.

+1

Buona roba qui. È fin troppo comune vedere la deglutizione dell'interrupt o semplicemente loggare e continuare senza reimpostare il flag di interrupt. Penso che le persone si sentano frustrate dal fatto che sia un'eccezione controllata che non sanno cosa fare, quindi non fanno nulla. –