Вопрос по android, multithreading, java, concurrency – Требуется более эффективный способ приостановки цикла

3

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

private boolean mIsCanceled = false;
private boolean mIsPaused = true; // TODO more efficient for processor way of pausing is required
private final Thread mTimerThread = new Thread(new Runnable() {
    @Override
    public void run() {
        while(!mIsCanceled){
            try {
                Thread.sleep(UPDATE_PERIOD);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!mIsPaused){
                doStep();
            }
        }
    }
});

public MyClass(){
    mTimerThread.start();
}

private void pause(){
    mIsPaused = true;
}

private void resume(){
    mIsPaused = false;
}

private void doStep(){
    // Some code
}

Пожалуйста, просто предоставьте альтернативную реализацию моего кода.

Постскриптум Среда Android OS 2.2+

Увеличение вашегоUPDATE_PERIOD уменьшит нагрузку на процессор. dasblinkenlight
Почему вы говорите, что "цикл таким образом все еще загружает процессор, когда процесс приостановлен"? Предполагается, что Thread.sleep () прекращает выполнение в течение заданного времени, и поэтому НЕ загружает процессор. в течение этого времени. Rajesh J Advani
1. Увеличение вашего UPDATE_PERIOD не работает для меня 2. Я бы предпочел, чтобы моя задача подождала, пока класс не был приостановлен, вместо проверки флага и выполнения цикла (даже если у меня спит цикл) Solvek

Ваш Ответ

4   ответа
5

wait/notify - мы все пытаемся уйти от этой архаичной системы.

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

CyclicBarrier - Должен быть создан заново каждый раз, когда он используется.

ReadWriteLock - Мое любимое. У вас может быть столько потоков, сколько вам нужно, и вы возобновите работу только после того, как все они вызвалиresume, Вы можете даже приостановить себя, если хотите.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * PauseableThread is a Thread with pause/resume and cancel methods.
 *
 * The meat of the process must implement `step`.
 *
 * You can either extend this and implement `step` or use the factory.
 *
 * Note that I cannot extend Thread because my resume will clash with Thread's deprecated one. 
 *
 * Usage: Either write a `Stepper` and run it in a `PausableThread` or extend `PausableThread` and call `blockIfPaused()` at appropriate points.
 */
public abstract class PauseableThread implements Runnable {
  // The lock.
  // We'll hold a read lock on it to pause the thread.
  // The thread will momentarily grab a write lock on it to pause.
  // This way you can have multiple pausers using normal locks.
  private final ReadWriteLock pause = new ReentrantReadWriteLock();
  // Flag to cancel the wholeprocess.
  private volatile boolean cancelled = false;
  // The exception that caused it to finish.
  private Exception thrown = null;

  @Override
  // The core run mechanism.
  public void run() {
    try {
      while (!cancelled) {
        // Block here if we're paused.
        blockIfPaused();
        // Do my work.
        step();
      }
    } catch (Exception ex) {
      // Just fall out when exception is thrown.
      thrown = ex;
    }
  }

  // Block if pause has been called without a matching resume.
  private void blockIfPaused() throws InterruptedException {
    try {
      // Grab a write lock. Will block if a read lock has been taken.
      pause.writeLock().lockInterruptibly();
    } finally {
      // Release the lock immediately to avoid blocking when pause is called.
      pause.writeLock().unlock();
    }

  }

  // Pause the work. NB: MUST be balanced by a resume.
  public void pause() {
    // We can wait for a lock here.
    pause.readLock().lock();
  }

  // Resume the work. NB: MUST be balanced by a pause.
  public void resume() {
    // Release the lock.
    pause.readLock().unlock();
  }

  // Stop.
  public void cancel() {
    // Stop everything.
    cancelled = true;
  }

  // start - like a thread.
  public void start() {
    // Wrap it in a thread.
    new Thread(this).start();
  }

  // Get the exceptuion that was thrown to stop the thread or null if the thread was cancelled.
  public Exception getThrown() {
    return thrown;
  }

  // Create this method to do stuff. 
  // Calls to this method will stop when pause is called.
  // Any thrown exception stops the whole process.
  public abstract void step() throws Exception;

  // Factory to wrap a Stepper in a PauseableThread
  public static PauseableThread make(Stepper stepper) {
    StepperThread pauseableStepper = new StepperThread(stepper);
    // That's the thread they can pause/resume.
    return pauseableStepper;
  }

  // One of these must be used.
  public interface Stepper {
    // A Stepper has a step method.
    // Any exception thrown causes the enclosing thread to stop.
    public void step() throws Exception;
  }

  // Holder for a Stepper.
  private static class StepperThread extends PauseableThread {
    private final Stepper stepper;

    StepperThread(Stepper stepper) {
      this.stepper = stepper;
    }

    @Override
    public void step() throws Exception {
      stepper.step();
    }
  }

  // My test counter.
  static int n = 0;

  // Test/demo.
  public static void main(String[] args) throws InterruptedException {

    try {
      // Simple stepper that just increments n.
      Stepper s = new Stepper() {
        @Override
        public void step() throws Exception {
          n += 1;
          Thread.sleep(10);
        }
      };
      PauseableThread t = PauseableThread.make(s);
      // Start it up.
      t.start();
      Thread.sleep(1000);
      t.pause();
      System.out.println("Paused: " + n);
      Thread.sleep(1000);
      System.out.println("Resuminng: " + n);
      t.resume();
      Thread.sleep(1000);
      t.cancel();
    } catch (Exception e) {
    }
  }
}

Edit: Код изменен, чтобы иметь более общее использование.

1

либо просто переключиться наScheduledExecutorService

Правильное использование wait () / notify () может быть непростым делом. Я настоятельно рекомендую & quot; Java Concurrency in Practice & quot; чтобы узнать больше о потоках.

0

используя монитор вместо сна. Вы просто делаете блоки в своем коде с синхронизированным ключевым словом. И последний объект, который действует как монитор. Посмотрите подробнее в API на мониторах.

Это в Java API, я рекомендую прочитать его, если хотите :)docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
Похоже, мониторы не доступны для Android. Solvek
0

что лучший способ здесь будет использоватьThread.wait для ожидания потока вместо сна, и использоватьThread.notify в ветке вы ждете. Больше информации здесь: http://www.javamex.com/tutorials/synchronization_wait_notify.shtml

Похожие вопросы