Generi c Поток, который может быть приостановлен / возобновлен и выполнять выполняемые пользователем действия - PullRequest
1 голос
/ 12 апреля 2020

AIM : для создания обобщенного c класса потока, который не зависит от вызывающего его родителя, может быть запущен / остановлен / приостановлен / возобновлен родительским классом, вызывающим его и выполнять пользовательские задачи (с помощью запуска)

МОИ ИССЛЕДОВАНИЯ : SO_1 SO_2 SO_3 SO_4 SO_5 SomeBlog SomeBlog2 OracleBlog

Проблема : из того, что я понял:

  • Запуск фонового потока : threadObj.start() выполнит операторы run() функции класса, реализующего интерфейс Runnable.

  • Остановка фонового потока : threadObj.interrupt() остановит выполнение потока

  • Приостановка потока : threadObj.wait() приостановит поток, хотя для этого требуется дополнительный механизм synchronised lock

  • Возобновление потока : threadObj.notifyAll() выпустит возобновить объект после того, как обработка synchronised lock mechanism

Таким образом, на основе этого я написал обобщенный класс c Thread, который должен запускать пользовательский набор задач и воспроизводить / приостанавливать / возобновлять / останавливать с помощью кнопок пользовательского интерфейса, НО ЭТО НЕ РАБОТАЕТ :

Generi c Thread. java


public class PausibleThread extends Thread {

    public static final String TAG ="PausibleThread>>";

    @Nullable
    PausibleRunnable runnable ;

    public PausibleThread(@Nullable Runnable target) {
        super(target);
        PausibleRunnable r = new PausibleRunnable(target);
        runnable=r;
    }

    @Override
    public synchronized void start() { super.start(); }

    public  synchronized void stopThread(){ this.interrupt(); }
    public  synchronized  void pauseThread(){ runnable.pause(); }
    public  synchronized  void resumeThread(){ runnable.resume(); }

PausibleRunnable. java:


import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class PausibleRunnable implements Runnable {
    private Object lockerObject;
    private boolean isPaused;
    private boolean isFinished;
    public static final String TAG="PausibleRunnable";

    @Nullable
    Runnable usrAction = null;

    public PausibleRunnable(@NonNull Runnable usrAction) {
        lockerObject = new Object();
        isPaused = false;isFinished = false;

        this.usrAction = usrAction;
    }

    public void run() {
        while (!isFinished) {

            if(isPaused) {
                runPauseLoop();
            }
            else {
                runUserAction();
                isFinished=true;
            }

        }
    }

    private void runPauseLoop() {
        synchronized (lockerObject) {
            while (isPaused) {

                try { lockerObject.wait(); }
                catch (InterruptedException e) { e.printStackTrace(); }

            }
        }
    }

    private void runUserAction() {
        if(usrAction !=null){ usrAction.run(); }
        else { Log.e(TAG, "run: userAction is NULL" ); }

    }




    public void pause() {
        synchronized (lockerObject) { isPaused = true; }
    }

    public void resume() {
        synchronized (lockerObject) {
            isPaused = false;
            lockerObject.notifyAll();
        }
    }


}

Ui создает Pausible Thread и реализует различные его функции:

    //full class implementation at : https://paste.ubuntu.com/p/cTpW5Wt3Fy/
    int totalRunTime = 20 * 5;
    Pausible thread bgThread;

    private void initThread() {
        Runnable r = () -> {
            try {
                while (totalRunTime > 0) {
                    Thread.sleep(500);
                    totalRunTime--;
                    updateUi();

                }
            }
            catch (Exception e) { e.printStackTrace(); }
        };
        bgThread = new PausibleThread(r);

    }

    private void updateUi() {
        String data = "TotalRunTime=" + totalRunTime;
        runOnUiThread(() -> tvTerminal.setText(data));
    }

    @Override
    public void onClick(View v) {
        if (bgThread == null) {
            makeShortToast("Can't perform action, bg thread is null");
            return;
        }

        if (v.getId() == fabPause.getId()) {bgThread.pauseThread(); }
        else if (v.getId() == fabResume.getId()) { bgThread.resumeThread(); }
        else if (v.getId() == fabStop.getId()) { bgThread.stopThread(); }
        else if (v.getId() == fabStart.getId()) { bgThread.start(); }

    }

Но это не работает. Почему? Я делаю здесь дикое предположение, но я думаю, что runnable только запускает действие пользователя для запуска большого размера l oop и не повторяет проверку на воспроизведение / паузу. Так что я должен делать?

Образец пользовательского интерфейса: https://i.imgur.com/kmj3Bwt.png

1 Ответ

1 голос
/ 12 апреля 2020

Вы спросили : «Но это не работает. Почему?»

Я отвечу : Ваше решение не работает, потому что вы всегда работаете в л oop внутри runUserAction. Вы никогда не выйдете из этого l oop, чтобы проверить, не приостановлены ли вы.

Боюсь, вам придется переделывать ваше решение для запуска usrAction в более коротких циклах, в противном случае вы либо потеряете состояние (при условии вы прерываете это l oop извне), что приведет к неопределенному поведению, ИЛИ вы выйдете из него только по окончании ИЛИ приостановите свой l oop в состояниях, которые вы на самом деле не хотите сделать паузу в [например, во время сетевого вызова - после возобновления вы получите исключение SocketTimeoutException].

Я бы предложил вам go с прежним подходом, поскольку он более элегантен.

Редактировать:

Еще одно возможное решение: каждая итерация внутри проверки usrAction для состояния PausableThread, т. Е. Посмотреть, приостановлено ли оно, остановлено или что-то еще.

Попробуйте это:

PausableRunnable. java

    public synchronized boolean canContinue() throws Exception {
        synchronized (lockerObject) {
            if (isPaused) {
                lockerObject.wait();
            }
            if (isFinished) {
                return false;
            }
            return true;
        }
    }

PausableThread. java

    public boolean canContinue() throws Exception {
        return runnable.canContinue();
    }

и приложение. java

private void initThread() {
        Runnable r = () -> {
            try {
                while (totalRunTime > 0) {
                    if (bgThread.canContinue()) { // <--- !!!!!!
                        Thread.sleep(200);
                        totalRunTime--;
                        updateUi();
                    }
                }
            }
            catch (Exception e) { e.printStackTrace(); }
        };
        bgThread = new PausibleThread(r);

    }

Таким образом, вы можете запустить ваше приложение Runnable и по-прежнему подчиняться PausableThread Состояния в то время, когда работоспособный может получить толчок. Т.е. до / после транзакции или другой части расчета, которая не должна прерываться.

Редактировать 2:

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

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