Nella mia applicazione ho un wrapper su un codice nativo, che viene chiamato tramite il bridge JNI. Questo codice nativo deve essere eseguito in thread separati (elaborazione parallela). Tuttavia il problema è che il codice a volte "si blocca", quindi il thread deve essere terminato "con la forza". Sfortunatamente non ho trovato alcun metodo "delicato" per farlo: il consiglio generale è di dire al codice in una discussione di uscire con garbo, ma non posso farlo con questo codice nativo (che è il codice di terze parti sopra).Termina una discussione che esegue un codice nativo
Io uso concomitante Java API per la presentazione compito:
Future<Integer> processFuture = taskExecutor.submit(callable);
try {
result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue();
}
catch (TimeoutException e) {
// How to kill the thread here?
throw new ExecutionTimeoutException("Execution timed out (max " + this.executionTimeout/60 + "min)");
}
catch (...) {
... exception handling for other cases
}
Future#cancel()
interromperà solo il filo, ma non terminerà esso. Così ho usato il seguente trucco:
class DestroyableCallable implements Callable<Integer> {
private Thread workerThread;
@Override
public Integer call() {
workerThread = Thread.currentThread();
return Integer.valueOf(JniBridge.process(...));
}
public void stopWorkerThread() {
if (workerThread != null) {
workerThread.stop();
}
}
}
DestroyableCallable callable = new DestroyableCallable();
Future<Integer> processFuture = taskExecutor.submit(callable);
try {
result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue();
}
catch (TimeoutException e) {
processFuture.cancel(true);
// Dirty:
callable.stopWorkerThread();
ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) taskExecutor;
logger.debug("poolSize: " + threadPoolTaskExecutor.getPoolSize() + ", maxPoolSize:"
+ threadPoolTaskExecutor.getMaxPoolSize() + ", activeCount:"
+ threadPoolTaskExecutor.getActiveCount());
}
throw new ...;
}
catch (...) {
... exception handling for other cases
}
Le domande/problemi con questo codice:
- E 'in generale, il modo giusto per farlo? Qualche altra alternativa più elegante?
activeCount
sull'operazione esecutore non viene ridotta, così compito esecutore ancora "pensa" quel filo esegue- ho dovuto aggiungere
workerThread != null
assegnostopWorkerThread()
metodo, come questa variabile rivelato esserenull
su alcuni casi. Non riesco a capire che cosa sono questi casi ...
Note:
- codice nativo non consuma descrittori di file (prese). Tutto è passato ad esso come blocco di dati e restituito allo stesso modo.
- Il codice nativo richiede un uso intensivo della CPU. Anche se garantisce di terminare, potrebbe volerci molto tempo.
Bounty modificare: L'approccio/suggerimento per rivisitare il codice nativo è chiaro, si prega di non offrire nella risposta. Ho bisogno di soluzioni/soluzioni in puro Java.
Grazie a un buon preavviso. Una piccola osservazione per il tuo post: poiché il thread avviato sta eseguendo il codice nativo, non ci sono blocchi (a meno che non lo si codi esplicitamente tramite JNI) che sono condivisi con Java-part. Quindi l'unica risorsa rischiosa sono i descrittori di file aperti (che non è il mio caso, poiché tutti i dati necessari vengono passati come blob e ripresi come stringa). L'unico problema è come dire a 'ThreadPoolTaskExecutor 'che il thread è veramente morto? Ho esaminato il codice: dovrebbe davvero catturare questo caso. –
Nessuna serratura - ok, potrebbe essere. Ma per quanto riguarda le perdite di memoria nel codice nativo? Puoi essere sicuro che la lib nativa sia stata progettata e codificata sufficientemente sicura per elaborare con garbo la terminazione anomala? Rilascia la memoria allocata su tutti i percorsi di uscita, incluso quello anormale? Cosa farai con la memoria, trapelata nel codice nativo?Stessa domanda su alcune risorse a livello di sistema operativo: cosa succede se la lib nativa alloca il blocco a livello di kernel? Inizia diversi thread aggiuntivi? – BegemoT
Sono d'accordo riguardo alle perdite di memoria sull'heap (come [i thread condividono lo stesso heap] (http://stackoverflow.com/questions/1665419)). Non sono un esperto, ma non penso che il thread utente possa essere interrotto in modalità kernel, altrimenti uccidere il thread danneggerebbe ad es. filesystem nel caso in cui il thread sia stato ucciso durante la scrittura di qualcosa sul disco. Quindi i blocchi a livello di kernel non rilasciati sarebbero molto tristi. Per il resto sostengo pienamente la tua posizione: il processo è più sicuro. Più sul ponte JNI è stato fatto da me da processo autonomo, quindi non è uno sforzo da ripristinare. –