2011-04-14 28 views
17

Tengo un servicio que inicia un subproceso y un ejecutable como tal.Detener/destruir un subproceso

t = new Thread(new Runnable() { 
    public void run() { 
     doSomething(); 
    } 
}); 

t.start(); 

La razón de la rosca es llevar a cabo una tarea asíncrona doSomething(). Por ahora, no nos preocupemos por la otra clase, AsyncTask. Lo probé y no funciona para mi caso. Editar: No puedo usar AsyncTask porque está diseñado solo para el subproceso de UI. Esta pieza de código tiene que operar dentro de un servicio, así pues no, no AsyncTask :(

doSomething() contiene algunas librerías externas por lo que el problema que estoy teniendo es que potencialmente puede ser colgado en uno de los comandos, sin devolver ningún valor (por lo tanto sin la comprobación de errores, incluso se puede hacer)

para evitar esto, voy a querer, en algún punto del tiempo, destruir el servicio.

stopService(new Intent("net.MyService.intent)); 

esto funciona bien y es fácil de verificar en el teléfono. Sin embargo, el subproceso que se creó anteriormente continuará ejecutándose incluso cuando t El Servicio que engendró es destruido.

Estoy buscando los comandos correctos para insertar en el Servicio onDestroy() que limpiará el hilo por mí.

t.destroy(); 
t.stop(); 

se han depreciado y provocan bloqueos de aplicaciones.

Tomé el código de alguna parte

@Override 
public void onDestroy() { 

    Thread th = t; 
    t = null; 

    th.interrupt(); 

    super.onDestroy(); 
} 

pero todavía no funciona, el hilo sigue funcionando. ¿Alguna ayuda chicos?

Respuesta

1

¿Has comprobado Java Thread Primitive Deprecation Documentación a la que se hace referencia en el Thread API JavaDoc. Encontrará algunos consejos para manejar su problema.

+3

Ese enlace, básicamente, itera todos los problemas y limitaciones que me enfrento. El enlace habla sobre cómo usar interrupt() en lugar de stop(), que es lo que estoy haciendo. El problema con interrupt() es que solo funciona en sleep(), wait() y join(). En mi código, la línea que cuelga no es ninguna de estas, y nunca se recupera, ni siquiera después de una hora. el hilo está atascado en esa línea, y ninguna interrupción puede rescatarlo. – foodman

1

¿por qué no utiliza un AsyncTask?

Una tarea se puede cancelar en cualquier momento por invocando cancelar (booleano). Invocando este método causará las siguientes llamadas a isCancelled() para devolver verdadero. Después de invocar este método, onCancelled (Object), en lugar de onPostExecute (Object) se invocará después de doInBackground (Object []) devuelve. Para asegurar que una tarea se cancelada tan pronto como sea posible, siempre debe comprobar el valor de retorno de isCancelled() periódicamente de doInBackground (Object []), si es posible (dentro de un bucle, por ejemplo.)

+2

No puedo usar AsyncTask debido a esto: "También es muy importante recordar que una instancia de AsyncTask debe crearse en el hilo de la interfaz de usuario y solo se puede ejecutar una vez" (developer.android.com/resources/articles/...) Mi tarea thread/async se ejecuta en un servicio por lo que no puede suceder - – foodman

+1

@foodman: Creo que entendiste mal lo que estaban tratando de decir, eso es solo ** si ** realizas operaciones que afectan el hilo de UI del 'onPostExecute' métodos etc. Si no tocas el hilo de la interfaz de usuario (que no serás porque lo estás ejecutando en un servicio), entonces está bien usar una tarea AsyncTask. –

1

respuesta alternativa

Utilice el siguiente código:

MyThread thread;  // class field 

crear e iniciar la hilo como lo hace ahora mismo.

thread = new MyThread(); 
thread.start(); 

Cuando el servicio está "señal" el hilo de dejar

public void onDestroy() { 
    // Stop the thread 
    thread.abort = true; 
    thread.interrupt(); 
} 

Aquí destruido es la implementación de hilos

//another class or maybe an inner class 
class MyThread extends Thread { 
    syncronized boolean abort = false; 

    //ugly, I know 
    public void run() { 
     try { 
      if(!abort) doA(); 
      if(!abort) doB(); 
      if(!abort) doC(); 
      if(!abort) doD(); 
     } catch (InterruptedException ex) { 
      Log.w("tag", "Interrupted!"); 
     } 
    } 
} 

Es posible que desee leer lo siguiente:

Creo que puede confiar en atrapar la excepción y no marcar abortar, pero decidí seguir así.

ACTUALIZACIÓN

que he visto esta muestra en codeguru:

public class Worker implements Runnable { 
    private String result; 
    public run() { 
     result = blockingMethodCall(); 
    } 
    public String getResult() { 
     return result; 
    } 
} 

public class MainProgram { 
    public void mainMethod() { 
     ... 
     Worker worker = new Worker(); 
     Thread thread = new Thread(worker); 
     thread.start(); 
     // Returns when finished executing, or after maximum TIME_OUT time 
     thread.join(TIME_OUT); 
     if (thread.isAlive()) { 
      // If the thread is still alive, it's still blocking on the methodcall, try stopping it 
      thread.interrupt(); 
      return null; 
     } else { 
      // The thread is finished, get the result 
      return worker.getResult(); 
     } 
    } 
} 
+0

El problema que está teniendo es que el equivalente de la función 'doA()' no está regresando limpiamente y, por lo tanto, el 'Thread' básicamente se cuelga en esa llamada al método. Como tal, no está llegando al siguiente paso, que sería la llamada 'if (! Abort)'. –

+0

pero si llama a thread.interrupt() debería generar una excepción que será atrapada por la cláusula try-catch. –

+0

Sí, yo también lo habría pensado, pero él dice que llamó 'th.interrupt();' en 'onDestroy()' y que el hilo continúa ejecutándose. –

9

El hilo destroy y stop métodos son inherentemente punto muerto propenso y no es seguro. Su existencia también da la ilusión de que podría haber alguna manera de detener otro hilo inmediatamente cuando algo más se lo indique.

Entiendo tu forma de pensar, desde tu punto de vista, su es un hilo principal, y cuando este hilo no ha recibido una respuesta de su hilo de trabajo en un momento que te gustaría matarlo y reiniciarlo, sin cuidando lo que depende de. Pero la razón por la que esos métodos están en desuso es que usted debería importar lo que está haciendo el hilo. Mucho.

¿Qué sucede si el hilo tiene un bloqueo alrededor de una variable que necesita usar más adelante? ¿Qué pasa si un hilo tiene un manejador de archivo abierto? En todos estos casos, y muchos más, simplemente detener el hilo en su operación actual dejaría las cosas en un caos, es muy probable que su aplicación simplemente se bloquee más adelante en la línea.

Por lo tanto, para que un hilo sea interrumpible o anulable o cancelable, debe gestionarlo solo. Si un hilo o una operación no le permiten interrumpirse, no puede interrumpirlo; se supone que hacerlo sería inseguro.

Si ejecutable es, literalmente,

public void run() { 
    doSomething(); 
} 

entonces no hay manera de interrumpirlo. Es de esperar que si doSomething eran una operación larga que podría haber una manera de cualquiera de interactuar con él de forma incremental con algo como

public void run() { 
    while (running) { 
     MyParser.parseNext(); 
    } 
} 

o para ser capaz de pasar de una variable por referencia, que indica si el hilo se interrumpe o no, y con suerte el método se interrumpiría en una ubicación adecuada.

Recuerde que el funcionamiento de blocking es bloqueante. No hay forma de evitarlo, no puedes cancelarlo en el medio.

+0

Gracias por la respuesta. La ÚNICA razón actual por la que estoy usando un subproceso es porque las operaciones doSomething() son asíncronas y colgarán el hilo principal en el teléfono, haciendo que todo el teléfono no se pueda usar durante ese tiempo. En realidad, no involucra ninguna variable que necesito, y no interactúa con nada fuera del hilo. Voy a mirar más allá en la AsyncTask que mencionaste antes – foodman

0

me gusta tomar el siguiente enfoque:

class MyHandler extends Handler { 
    final Semaphore stopEvent = new Semaphore(0); 

    @Override 
    public void handleMessage(Message msg) { 
     try { 
      while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) { 
       doSomething(); 

       if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) { 
        break; 
       } 
      } 
     } catch (InterruptedException ignored) { 
     } 

     stopSelf(); 
    } 
} 

En OnDestroy servicio basta con soltar el stopevent:

@Override 
public void onDestroy() { 
    myHandler.stopEvent.release(); 
    myHandler = null; 

    super.onDestroy(); 
} 
0

mejor utilizar stopThread variable global, deje de hilo variable de una vez cambiado a true.

btnStop.setOnClickListener(new OnClickListener() {   
     @Override 
     public void onClick(View arg0){ 
      stopThread = true; 
     } 
    }); 


public void run() { 
     while (!stopThread) { 
      //do something 
     } 
} 
0

Creo que la mejor manera de crear y comunicarse con otro hilo es utilizar un AsyncTask. Aquí está un ejemplo de uno:

public class Task extends AsyncTask<Void, Void, Void> { 

    private static final String TAG = "Task"; 

    private boolean mPaused; 

    private Runnable mRunnable; 

    public Task(Runnable runnable) { 
     mRunnable = runnable; 
     play(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
     while (!isCancelled()) { 
      if (!mPaused) { 

       mRunnable.run(); 
       sleep(); 
      } 
     } 
     return null; 
    } 

    private void sleep() { 
     try { 
      Thread.sleep(10); 
     } catch (InterruptedException e) { 
      Log.w(TAG, e.getMessage()); 
     } 
    } 

    public void play() { 
     mPaused = false; 
    } 

    public void pause() { 
     mPaused = true; 
    } 

    public void stop() { 
     pause(); 
     cancel(true); 
    } 

    public boolean isPaused() { 
     return mPaused; 
    } 

} 

Ahora puede utilizar fácilmente esta clase, y comenzar el hilo por la escritura:

Task task = new Task(myRunnable); 
task.execute((Void) null); 

Junto con esto se puede pausar o detener el hilo de bucle fácil:

Ejemplo de hacer una pausa y reproducción de la rosca:

mButton.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     if (task.isPaused()) { 
      task.play(); 
     } else { 
      task.pause(); 
     } 
    } 
}); 

Ejemplo de detener y st diseñando el hilo:

mButton.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     if (task.isCancelled()) { 
      task = new Task(myRunnable); 
      task.execute((Void) null); 
     } else { 
      task.stop(); 
     } 
    } 
}); 
Cuestiones relacionadas