Остановка / уничтожение темы - PullRequest
19 голосов
/ 14 апреля 2011

У меня есть служба, которая запускает поток и Runnable, как это.

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

t.start();

Причиной потока является выполнение асинхронной задачи doSomething () .А пока давайте не будем беспокоиться о другом классе AsyncTask.Я попробовал это, и это не работает для моего случая.Изменить: я не могу использовать AsyncTask, потому что он предназначен только для потока пользовательского интерфейса.Этот фрагмент кода должен работать внутри Службы, так что нет, AsyncTask: (

doSomething () содержит некоторые внешние библиотеки, поэтому у меня проблема в том, что он потенциально может быть подвешенпо одной из команд, без возврата какого-либо значения (следовательно, проверка ошибок невозможна)

Чтобы обойти это, я хочу в какой-то момент уничтожить Службу.

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

Это прекрасно работает и легко проверяется на телефоне. Однако созданный выше поток продолжает работать, даже если служба, которая его породила, уничтожена.

Я ищуправильные команды для вставки в службу onDestroy () , которая очистит поток для меня.

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

устарели и вызывают сбой приложения.

Я взялэтот код откуда-то

@Override
public void onDestroy() {

    Thread th = t;
    t = null;

    th.interrupt();

    super.onDestroy();
}

, но он все еще не работает, поток продолжает работать. Любая помощь, ребята?

Ответы [ 7 ]

11 голосов
/ 14 апреля 2011

Методы потоков destroy и stop по своей природе подвержены тупику и небезопасны.Их существование также создает иллюзию того, что может быть какой-то способ немедленно прекратить другой поток, когда что-то еще скажет.

Я понимаю ваше мышление, с вашей точки зрения, это один из основных потоков, и когда этот потокВы не получили ответ от своего рабочего потока в то время, как вы хотели бы его убить и перезапустить, , не заботясь о том, что он до .Но причина, по которой эти методы устарели, заключается в том, что вы должны заботиться о том, что задумал поток.Много.

Что если у потока есть блокировка вокруг переменной, которую вам нужно использовать позже?Что если у потока открыт дескриптор файла?Во всех этих случаях и во многих других случаях простое прекращение потока во время его текущей операции может привести к путанице - вполне вероятно, что ваше приложение просто рухнет дальше.

Итак, для того, чтобы поток былпрерываемый или отменяемый или останавливаемый, он должен справиться с этим сам.Если поток или операция не могут прервать себя, то вы не можете прервать его - предполагается, что это будет небезопасно.

Если вы работаете, буквально

public void run() {
   doSomething();
}

тогда нет способа прервать это.Можно было бы надеяться, что если бы doSomething была длительной операцией, то мог бы существовать способ либо поэтапного взаимодействия с ним с чем-то вроде

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

, либо возможность передачи переменной по ссылке, которая указывает, является липоток прерывается или нет, и, надеюсь, метод прервет себя в подходящем месте.

Помните, что операция blocking блокирует.Нет способа обойти это, вы не можете отменить его частично.

2 голосов
/ 14 апреля 2011

Альтернативный ответ

Используйте следующий код:

MyThread thread;     // class field

Создайте и запустите поток, как вы делаете это прямо сейчас.

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

Когда служба уничтожена, «сигнализировать» потоку о выходе

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

Вот реализация потока

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

Возможно, вы захотите прочитать следующее:

Я думаю, что вы можете рассчитывать на перехват исключения, а не на проверку отмены, но я решилпродолжайте в том же духе.

ОБНОВЛЕНИЕ

Я видел этот пример в 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(); 
        }
    }
}
1 голос
/ 14 апреля 2011

почему бы вам не использовать AsyncTask ?

Задача может быть отменена в любое время вызов отмены (булево). Вызов этот метод вызовет последующее вызывает isCancelled (), чтобы вернуть true. После вызова этого метода, onCancelled (Object) вместо onPostExecute (Object) будет вызван после doInBackground (Object []) возвращается. Чтобы убедиться, что задача отменить как можно быстрее, вы всегда следует проверять возвращаемое значение isCancelled () периодически из doInBackground (Object []), если возможно (например, внутри цикла.)

1 голос
/ 14 апреля 2011

Проверяли ли вы Примитив нити Java Thread Документация, на которую есть ссылка в API-интерфейсе Thread JavaDoc.Вы найдете несколько советов для решения вашей проблемы.

0 голосов
/ 16 августа 2017

Я думаю, что лучший способ создать и связаться с другим потоком - это использовать AsyncTask . Вот пример одного:

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

}

Теперь вы можете легко использовать этот класс и запустить поток, написав:

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

Наряду с этим вы можете легко приостановить или остановить зацикливание потока:

Пример приостановки и воспроизведения потока:

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

Пример остановки и запуска потока:

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 голосов
/ 07 июля 2016

Лучше использовать глобальную переменную stopThread, останавливать поток при изменении переменной на true.

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


public void run() {
        while (!stopThread) {
           //do something
        }
}
0 голосов
/ 29 декабря 2015

Мне нравится использовать следующий подход:

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

На сервисе onDestroy просто отпустите stopEvent:

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

    super.onDestroy();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...