AsyncTask використовує шаблон пулу потоків для запуску матеріалів з doInBackground (). Проблема спочатку (у ранніх версіях ОС Android) розмір пулу становив лише 1, тобто не було паралельних обчислень для групи AsyncTasks. Але згодом вони виправили це і тепер розмір становить 5, тож максимум 5 AsyncTasks можуть працювати одночасно. На жаль, я не пам’ятаю, в якій версії вони саме це змінили.
ОНОВЛЕННЯ:
Ось що про це говорить поточний (2012-01-27) API:
При першому введенні AsyncTasks виконувались послідовно на одному фоновому потоці. Починаючи з DONUT, це було змінено на пул потоків, що дозволяють паралельно працювати безлічі завдань. Після HONEYCOMB планується змінити цю функцію на один потік, щоб уникнути поширених помилок програми, викликаних паралельним виконанням. Якщо ви справді хочете паралельного виконання, ви можете використовувати версію цього методу ExecuteOnExecutor (Виконавець, Параметри ...) з THREAD_POOL_EXECUTOR; однак, дивіться коментар щодо попереджень щодо його використання.
DONUT - Android 1.6, HONEYCOMB - Android 3.0.
ОНОВЛЕННЯ: 2
Дивіться коментар kabuko
від Mar 7 2012 at 1:27
.
Виявляється, що для API, де використовується "пул потоків, що дозволяють паралельно працювати декільком завданням" (починаючи з 1.6 і закінчуючи 3.0), кількість одночасно запущених AsyncTasks залежить від того, скільки завдань вже було передано для виконання, але ще не закінчили свої doInBackground()
.
Це перевірено / підтверджено мною 2.2. Припустимо, у вас є спеціальний AsyncTask, який просто спить секунду doInBackground()
. AsyncTasks використовує внутрішню чергу фіксованого розміру для зберігання відкладених завдань. За замовчуванням розмір черги 10. Якщо ви запустите 15 своїх власних завдань поспіль, то перші 5 введуть їх doInBackground()
, а решта чекатимуть у черзі на безкоштовну робочу нитку. Як тільки будь-яке з перших 5 завершиться і тим самим випустить робочу нитку, завдання з черги почне виконання. Так що в цьому випадку не більше 5 завдань будуть виконуватися одночасно. Однак якщо ви запускаєте 16 своїх власних завдань поспіль, то перші 5 вводять їх doInBackground()
, решта 10 потраплять у чергу, але для 16-го буде створений новий робочий потік, тому він почне виконання негайно. Так що в цьому випадку не більше 6 завдань будуть виконуватися одночасно.
Існує обмеження кількості завдань, які можна виконати одночасно. Оскільки AsyncTask
використовується виконавець пулу потоків з обмеженою максимальною кількістю робочих потоків (128), а черга завдань із запізненням має фіксований розмір 10, якщо ви спробуєте виконати більше 138 своїх спеціальних завдань, з якими буде завершено роботу програми java.util.concurrent.RejectedExecutionException
.
Починаючи з 3.0, API дозволяє використовувати власний виконавець пулу потоків за допомогою AsyncTask.executeOnExecutor(Executor exec, Params... params)
методу. Це дозволяє, наприклад, налаштувати розмір черги із затримкою завдань, якщо за замовчуванням 10 це не те, що потрібно.
Як згадує @Knossos, є можливість використовувати AsyncTaskCompat.executeParallel(task, params);
з бібліотеки підтримки v.4 паралельно виконання завдань паралельно, не турбуючись з рівнем API. Цей метод застарів у рівні API 26.0.0.
ОНОВЛЕННЯ: 3
Ось простий тестовий додаток для гри з низкою завдань, послідовним та паралельним виконанням: https://github.com/vitkhudenko/test_asynctask
ОНОВЛЕННЯ: 4 (спасибі @penkzhou за вказівку на це)
Починаючи з Android 4.4 AsyncTask
поводиться інакше, ніж описано в розділі UPDATE: 2 . Існує виправлення, щоб не AsyncTask
створювати занадто багато ниток.
До Android 4.4 (API 19) AsyncTask
були такі поля:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
В Android 4.4 (API 19) вищезазначені поля змінені на це:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
Ця зміна збільшує розмір черги до 128 елементів та зменшує максимальну кількість потоків до кількості ядер CPU * 2 + 1. Програми все ще можуть подавати однакову кількість завдань.