wake-up-neo.com

Einen Thread stoppen/zerstören

Ich habe einen Service, der einen Thread und einen Runnable so startet.

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

t.start();

Der Grund für den Thread ist die Ausführung einer Async-Aufgabe doSomething () . Für jetzt lassen Sie uns über die andere Klasse AsyncTask nicht sorgen. Ich habe es ausprobiert und es funktioniert nicht für meinen Fall. Edit: Ich kann AsyncTask nicht verwenden, da es nur für den UI-Thread gedacht ist. Dieser Code muss innerhalb eines Service funktionieren, also keine AsyncTask :(

doSomething () enthält einige externe Bibliotheken, daher besteht das Problem, dass es möglicherweise an einem der Befehle aufgehängt werden kann, ohne dass ein Wert zurückgegeben wird (daher kann auch keine Fehlerprüfung durchgeführt werden).

Um dies zu umgehen, möchte ich den Dienst irgendwann zerstören.

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

Dies funktioniert gut und kann am Telefon leicht überprüft werden. Der Thread, der oben erstellt wurde, wird jedoch weiterhin ausgeführt, auch wenn der Dienst, der ihn erstellt hat, zerstört wird.

Ich suche also nach den richtigen Befehlen zum Einfügen in den Dienst onDestroy () , der den Thread für mich aufräumt.

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

werden beide abgeschrieben und verursachen Anwendungsabstürze.

Ich habe diesen Code irgendwo mitgenommen

@Override
public void onDestroy() {

    Thread th = t;
    t = null;

    th.interrupt();

    super.onDestroy();
}

aber es funktioniert immer noch nicht, der Thread läuft weiter. Irgendwelche Jungs helfen?

19
foodman

Die Methoden destroy und stop des Threads sind grundsätzlich anfällig für Deadlocks und nicht sicher. Ihre Existenz gibt auch die Illusion, dass es eine Möglichkeit gibt, einen anderen Thread sofort zu stoppen, wenn es von etwas anderem dazu aufgefordert wird.

Ich verstehe, wie Sie denken, aus Ihrer Sicht ist dies ein Hauptthread, und wenn dieser Thread in einiger Zeit keine Antwort von seinem Arbeitsthread erhalten hat, möchten Sie ihn beenden und neu starten, ohne sich dessen zu kümmern bis zu . Aber der Grund, warum diese Methoden veraltet sind, ist, dass Sie sollten sich darum kümmern, was der Thread vorhat. Viel.

Was ist, wenn der Thread eine Variable für eine Variable enthält, die Sie später verwenden müssen? Was ist, wenn ein Thread ein Dateihandle geöffnet hat? In all diesen Fällen und in vielen anderen Fällen würde das bloße Stoppen des Threads bei seiner aktuellen Operation die Dinge in Unordnung bringen - es ist sehr wahrscheinlich, dass Ihre Anwendung später weiter abstürzt.

Damit ein Thread unterbrechbar oder abbruchfähig oder anhalten werden kann, muss er dies selbst verwalten. Wenn ein Thread oder eine Operation keine Möglichkeit bietet, sich selbst zu unterbrechen, können Sie ihn nicht unterbrechen. Dies wird als nicht sicher angesehen.

Wenn Sie lauffähig sind, ist es buchstäblich 

public void run() {
   doSomething();
}

dann gibt es keine Möglichkeit, es zu unterbrechen. Man würde hoffen, dass, wenn doSomething eine lange Operation wäre, es eine Möglichkeit gibt, entweder inkrementell mit so etwas zu interagieren

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

oder in der Lage sein, eine Variable per Referenz zu übergeben, die angibt, ob der Thread unterbrochen ist oder nicht, und hoffentlich würde sich das Verfahren an geeigneter Stelle selbst unterbrechen.

Denken Sie daran, dass eine blocking-Operation blockiert. Es gibt keine Möglichkeit, das zu umgehen, Sie können es nicht zwischendurch abbrechen.

11
Joseph Earl

Alternative Antwort

Verwenden Sie den folgenden Code:

MyThread thread;     // class field

Erstellen und starten Sie den Thread, wie Sie es gerade tun.

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

Wenn der Dienst zerstört wird, "signalisieren" Sie dem Thread, dass er beendet werden soll

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

Hier ist die Thread-Implementierung

//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!");
       }
    }
}

Vielleicht möchten Sie folgendes lesen:

Ich denke, Sie konnten sich darauf verlassen, dass Sie die Ausnahme abfangen und nicht auf Abbruch prüfen, aber ich entschied mich dafür, dies zu belassen.

UPDATE

Ich habe dieses Beispiel in codeguru gesehen:

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(); 
        }
    }
}
2
Pedro Loureiro

Haben Sie die Java Thread Primitive Deprecation -Dokumentation überprüft, auf die im Thread API JavaDoc verwiesen wird. Sie werden einige Hinweise finden, um Ihr Problem zu lösen.

1
FrVaBe

warum benutzt du nicht eine AsyncTask ?

Eine Aufgabe kann jederzeit abgebrochen werden, indem Sie Aufrufen (boolean). Durch Aufrufen von Dieser Methode werden nachfolgende - Aufrufe von isCancelled () zurückgegeben. Nach dem Aufrufen dieser Methode OnCancelled (Object) anstelle von .] onPostExecute (Object) wird aufgerufen, nachdem doInBackground (Object []) zurückgegeben wird. Um sicherzustellen, dass eine Aufgabe So schnell wie möglich abgebrochen wird, sollten Sie Den Rückgabewert Von isCancelled () regelmäßig von DoInBackground (Object []) aus überprüfen. ), wenn möglich (zum Beispiel innerhalb einer Schleife.)

1
Pedro Loureiro

Verwenden Sie besser die globale Variable stopThread. Stoppen Sie den Thread, sobald die Variable in true geändert wurde.

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


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

Ich denke, der beste Weg zum Erstellen und Kommunizieren mit einem anderen Thread ist die Verwendung einer AsyncTask . Hier ist ein Beispiel:

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;
    }

}

Sie können diese Klasse jetzt problemlos verwenden und den Thread durch Schreiben starten:

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

Zusammen mit diesem können Sie den Thread leicht anhalten oder an dem Schleifen hindern:

Beispiel für das Anhalten und Abspielen des Threads:

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

Beispiel für das Stoppen und Starten des Threads:

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();
        }
    }
});
0
Anthony Cannon

Ich gehe gerne wie folgt vor:

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();
    }
}

Bei Service onDestroy veröffentlichen Sie einfach das stopEvent:

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

    super.onDestroy();
}
0
Skarllot