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



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



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



вот код:



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



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");
}
}
593   3  

3 ответов:

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

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

до 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 поскольку очередь станет полной, и исполнитель будет нажат, чтобы создать больше рабочих потоков. Если вы продолжили с длительной задачей, вы должны увидеть, что 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() {
       // e.g. 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 под капотом executor, но без сериализации задач, или вы даже можете создать свой собственный исполнитель и предоставить его здесь. Однако обратите внимание на предупреждение в javadocs.

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

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

Comments

    Ничего не найдено.