Ограничения потоков Android AsyncTask?

Я разрабатываю приложение, где мне нужно обновлять некоторую информацию каждый раз, когда пользователь входит в систему, я также использую базу данных в телефоне. Для всех этих операций (обновления, извлечение данных из db и т. Д.) Я использую async-задачи. До сих пор я не понимал, почему я не должен их использовать, но недавно я испытал, что, если я выполню некоторые операции, некоторые из моих задач async просто останавливаются на предварительном выполнении и не переходят на doInBackground. Это было слишком странно, чтобы оставить это так, поэтому я разработал еще одно простое приложение, чтобы проверить, что случилось. И, что довольно странно, я получаю такое же поведение, когда количество асинхронных задач достигает 5, а шестое – при предварительном выполнении.

Есть ли у android ограничения asyncTasks в Activity / App? Или это просто ошибка, и ее следует сообщить? Кто-нибудь испытывал ту же проблему и, возможно, нашел обходное решение?

Вот код:

Просто создайте 5 из этих потоков для работы в фоновом режиме:

private class LongAsync extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); isRunning = true; } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); while (isRunning) { } return null; } @Override protected void onPostExecute(String result) { Log.d("TestBug","onPostExecute"); } } 

А затем создайте этот поток. Он войдет в preExecute и повесит (он не пойдет в doInBackground).

 private class TestBug extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); waiting = new ProgressDialog(TestActivity.this); waiting.setMessage("Loading data"); waiting.setIndeterminate(true); waiting.setCancelable(true); waiting.show(); } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); return null; } @Override protected void onPostExecute(String result) { waiting.cancel(); Log.d("TestBug","onPostExecute"); } } 

Все AsyncTasks контролируются внутри общего (статического) ThreadPoolExecutor и LinkedBlockingQueue . Когда вы вызываете execute в AsyncTask, ThreadPoolExecutor выполнит его, когда он будет готов некоторое время в будущем.

«Когда я готов?» Поведение ThreadPoolExecutor контролируется двумя параметрами: размером пула ядра и максимальным размером пула . Если в настоящее время активны нити основного потока пула, а новое задание приходит, исполнитель создаст новый поток и выполнит его немедленно. Если есть хотя бы потоки основного потока пула, он попытается поставить в очередь задание и подождать, пока не будет доступ к свободному потоку (т.е. пока не будет выполнено другое задание). Если невозможно поставить в очередь задание (очередь может иметь максимальную емкость), он создаст новый поток (вплоть до максимальных потоков пулов) для выполняемых заданий. Непростые незанятые потоки могут быть в конечном итоге выведены из строя согласно Параметр тайм-аута keep-alive.

До Android 1.6 размер основного пула составлял 1, а максимальный размер пула – 10. Начиная с Android 1.6 размер ядра составляет 5, а максимальный размер пула – 128. Размер очереди равен 10 в обоих случаях. Таймаут keep-alive составлял 10 секунд до 2,3 и 1 секунду с тех пор.

Учитывая все это, теперь становится ясно, почему AsyncTask будет только выполнять 5/6 ваших задач. 6-я задача находится в очереди до тех пор, пока не завершится одна из других задач. Это очень хорошая причина, по которой вы не должны использовать AsyncTasks для длительных операций – это предотвратит запуск других AsyncTasks.

Для полноты, если вы повторили упражнение с более чем 6 заданиями (например, 30), вы увидите, что более 6 будет вводить doInBackground когда очередь будет заполнена, а исполнитель будет doInBackground для создания большего количества рабочих потоков. Если вы сохранили долгосрочную задачу, вы должны увидеть, что 20/30 становятся активными, а еще 10 находятся в очереди.

@antonyt имеет правильный ответ, но если вы ищете простое решение, вы можете проверить иглу.

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

 Needle.onBackgroundThread().withThreadPoolSize(3).execute(new UiRelatedTask<Integer>() { @Override protected Integer doWork() { int result = 1+2; return result; } @Override protected void thenDoUiRelatedWork(Integer result) { mSomeTextView.setText("result: " + result); } }); 

Или такие вещи, как

 Needle.onMainThread().execute(new Runnable() { @Override public void run() { // eg change one of the views } }); 

Это может сделать даже намного больше. Проверьте это на GitHub .

Обновление : начиная с API 19 размер пула основных потоков был изменен, чтобы отображать количество процессоров на устройстве с минимальным значением 2 и максимум 4 при запуске, при этом он увеличивался до максимального значения CPU * 2 +1 – Ссылка

 // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 

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

 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) 

Вы можете предоставить Исполнителя для выполнения ваших задач. Вы можете предоставить THREAD_POOL_EXECUTOR под исполнителем капота, но без сериализации задач, или вы даже можете создать своего собственного Исполнителя и предоставить его здесь. Однако внимательно обратите внимание на предупреждение в Javadocs.

Предупреждение. Разрешение нескольких задач, выполняемых параллельно из пула потоков, обычно не является тем, что нужно, потому что порядок их работы не определен. Например, если эти задачи используются для изменения общего состояния (например, для записи файла из-за нажатия кнопки), нет никаких гарантий в отношении порядка модификаций. Без тщательной работы в редких случаях, когда более новая версия данных может быть переписана более старой, что приводит к неясным проблемам потери данных и стабильности. Такие изменения лучше всего выполнять в серийном режиме; Чтобы гарантировать, что такая работа сериализована независимо от версии платформы, вы можете использовать эту функцию с SERIAL_EXECUTOR.

Еще одна вещь, которую следует отметить, заключается в том, что обе структуры, предоставленные Executors THREAD_POOL_EXECUTOR и ее серийная версия SERIAL_EXECUTOR (по умолчанию для AsyncTask), являются статическими (конструкциями уровня класса) и, следовательно, распределены между всеми экземплярами AsyncTask (ов) в процессе вашего приложения.

Intereting Posts