2014-01-22 3 views
11

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

Моя реализация основана на классе ThreadPoolExecutor наряду с LinkedBlockingQueue.

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

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

public class TestService extends Service { 
    // Sets the initial threadpool size to 3 
    private static final int CORE_POOL_SIZE = 3; 

    // Sets the maximum threadpool size to 3 
    private static final int MAXIMUM_POOL_SIZE = 3; 

    // Sets the amount of time an idle thread will wait for a task before terminating 
    private static final int KEEP_ALIVE_TIME = 1; 

    // Sets the Time Unit to seconds 
    private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; 

    // A queue of Runnables for the uploading pool 
    private final LinkedBlockingQueue<Runnable> uploadQueue = new LinkedBlockingQueue<Runnable>(); 

    // A managed pool of background upload threads 
    private final ThreadPoolExecutor uploadThreadPool = new ThreadPoolExecutor(
      CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, 
      uploadQueue) { 

     @Override 
     protected void afterExecute(Runnable r, Throwable t) { 
      super.afterExecute(r, t); 

      if (getActiveCount() == 1 && getQueue().size() == 0) { 
       // we're the last Runnable around + queue is empty, service can be 
       // safely stopped. 
       TestService.this.stopSelf(); 
      } 
     } 
    }; 

    @Override 
    public IBinder onBind(Intent intent) { 
     return null; 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     // execute a new Runnable 
     uploadThreadPool.execute(new TestRunnable()); 

     /** 
     * Indicating that if Android has to kill off this service (i.e. low memory), 
     * it should not restart it once conditions improve. 
     */ 
     return START_NOT_STICKY; 
    } 

    @Override 
    public void onDestroy() { 
     uploadThreadPool.shutdownNow(); 
     uploadQueue.clear(); 

     super.onDestroy(); 
    } 
} 

У меня есть кое-что, о чем я еще не уверен.

  1. Предполагая, что onDestroy называли, это с уверенностью предположить, что моя реализация будет прерывать все запущенные потоки и безопасно очистить незавершенные задачи, не прерывая каким-то образом с внедрением в ThreadPoolExecutor класса? причина, по которой я спрашиваю, связана с тем, что очередь связана с исполнителем, и, возможно, shutdownNow является асинхронным и зависит от состояния очереди. Есть ли лучший способ сделать это?

  2. Я правильно использую эту логику внутри onDestroy? из моего опыта есть некоторые случаи, когда служба убита (т. е. низкая память), и этот обратный вызов не вызывается. Должен ли я выполнять аналогичный подход и в других местах?

  3. Было бы лучше объявить мою очередь & членами-исполнителями как статическими? - Как указано @TheTwo "Excecutor cannot be re-used once shutdown is called".

  4. ThreadPoolExecutor класс ожидает BlockingQueue, каковы плюсы/минусы использования других типов BlockingQueue реализаций (т.е. ArrayBlockingQueue)?

  5. Относительно того, как я обнаруживаю, когда очередь пуста & больше нет ожидающих задач (в частности, внутри обратного вызова afterExecute). Это лучший способ сделать это? Или я могу получить указание, что очередь пуста, а задачи закончены по-другому?

Цените любую помощь!

ответ

5

Я думаю, что вы пытаетесь внедрить сервис, который вводит множество проблем, но не решает. Эффективно вы уменьшаете код вызова на одну строку - создание Исполнителя - но убираете возможность его тонкого контроля. Нет никакой выгоды при планировании многих задач, поскольку это уже разрешено планировщиком потоков ОС. Кроме того, злоумышленник может разбить несколько других программ, просто добавив достаточно while(true) sleep(100); петель.

На вопросы:

  1. Вы не можете убедиться, что все потоки правильно прерываться когда-либо, потому что нет никакого способа, чтобы прервать поток, который не правильно смотреть флаг прерывания. A while(true) ; не может быть прерван, кроме System.exit(). Теоретически вы можете остановить поток, но эта функция устарела по какой-то причине, поскольку она может оставить фактическую задачу в незавершенном/незавершенном состоянии (то есть, оставив TCP-соединения полуоткрытыми).

  2. Нет, вы неправильно выполняете это. Если однажды задачи останутся int, то очередь просто исчезнет в пустоте, а затем Excecutor не сможет повторно использоваться после вызова shutdown. Поэтому вам, по крайней мере, нужно создать новый экземпляр Excecutor при запуске службы, и вам действительно нужно выяснить, что делать с оставшимися задачами.

  3. Нет, из-за 2.

  4. Плюсы/минусы типов списков зависит от вашего сценария использования. ArrayList имеет более высокую стоимость при росте/сокращении, но более низкую стоимость при индексировании определенного элемента (indexOf), а связанный список - наоборот. Так как ваша очередь всегда добавляет хвост, не волнует какой-либо другой элемент, а первый, и он часто растет/сокращается, связанный список - лучший выбор.

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

+1

re # 5, следуя http://www.youtube.com/watch?v=xHXn3Kg2IQE У меня сложилось впечатление, что, как только служба завершила свою работу, ее следует остановить, сохранить аккумулятор и сообщить системе, что он может высвободить ресурсы и что служба больше не используется. Разве андроид не принимает решения об уничтожении определенных запущенных компонентов на основе такой информации? – cdroid

+0

Если вы планируете никогда больше не пользоваться услугой, рекомендуется очистить ее. Но представьте себе следующее: 'for (int i = 0; i TwoThe

2

  1. No shutdownNow пытается прервать текущие активные задачи. Нет гарантий, что он сможет это сделать
  2. Да, это так. Из документации:

    После того, как ни одна из этих ситуаций удержания, OnDestroy сервиса() метод вызывается и обслуживание эффективно прекращается. Все очистка (стопорные нити, незарегистрированные приемники) должна быть завершена по возвращении из onDestroy().

  3. нет причин, чтобы эти члены были объявлены как статические.Элементы static ассоциированы с классом withe, а не с каким-либо объектом, а общий используется для совместного использования одного и того же статического члена между различными (услугами в вашем случае) экземплярами.
  4. Вы должны внимательно прочитать документацию обо всех возможных реализациях интерфейса BlockingQueue<E>, но я сомневаюсь, что для использования в обычных случаях вы увидите различия с точки зрения производительности.

+3

Как 4, Вы можете рассмотреть возможность использования 'утилита Executors' класса (см http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html) в создайте экземпляр ThreadPoolExecutor. Например, 'Executors # newFixedThreadPool (int nThreads)' –

+0

@blackbelt причина, по которой я спрашиваю о # 4, состоит в том, что услугу можно остановить и запустить несколько раз и, возможно, иметь возможность использовать один и тот же экземпляр. – cdroid

+0

Обычно это не ведет нигде, но, возможно, нисходящий может оставить комментарий, чтобы я мог улучшить свой ответ? – Blackbelt

4

Несколько лет назад я написал this article на управление потоками в службе. Вы можете использовать само программное обеспечение или просто получить некоторые идеи о том, как сделать это самостоятельно.

+4

Пожалуйста, добавьте некоторые детали из статьи в свой ответ.Если ссылка будет мертвой, это сделает ваш ответ бесполезным. – Luksprog

+0

Проблема с этим в статье объясняет, почему «зачем» нужен менеджер и хорошее программное решение. Добавление детализации означало бы здесь много слов и без ссылки на программное обеспечение, которое оно бесполезно. – edharned

Смежные вопросы