AsyncTask używa wzorca puli wątków do uruchamiania rzeczy z doInBackground (). Problem polega na tym, że początkowo (we wczesnych wersjach systemu operacyjnego Android) wielkość puli wynosiła zaledwie 1, co oznacza brak równoległych obliczeń dla grupy AsyncTasks. Ale później to naprawili i teraz rozmiar wynosi 5, więc maksymalnie 5 zadań AsyncTask może działać jednocześnie. Niestety nie pamiętam, w jakiej wersji dokładnie to zmienili.
AKTUALIZACJA:
Oto, co mówi na ten temat bieżący (2012-01-27) interfejs API:
Po raz pierwszy AsyncTasks były wykonywane szeregowo w jednym wątku w tle. Począwszy od DONUT, zmieniono to na pulę wątków, umożliwiając równoległe wykonywanie wielu zadań. Po HONEYCOMB planuje się zmienić to z powrotem na pojedynczy wątek, aby uniknąć typowych błędów aplikacji spowodowanych równoległym wykonywaniem. Jeśli naprawdę chcesz wykonywać równolegle, możesz użyć wersji tej metody executeOnExecutor (Executor, Params ...) z THREAD_POOL_EXECUTOR; jednak zobacz komentarz tam ostrzeżenia o jego użyciu.
DONUT to Android 1.6, HONEYCOMB to Android 3.0.
AKTUALIZACJA: 2
Zobacz komentarz kabuko
od Mar 7 2012 at 1:27
.
Okazuje się, że w przypadku interfejsów API, w których używana jest „pula wątków umożliwiająca równoległe działanie wielu zadań” (od wersji 1.6 do wersji 3.0) liczba jednoczesnych uruchomień AsyncTasks zależy od liczby zadań, które zostały już przekazane do wykonania, ale jeszcze nie skończyli doInBackground()
.
To zostało przetestowane / potwierdzone przeze mnie w wersji 2.2. Załóżmy, że masz niestandardową funkcję AsyncTask, która tylko śpi sekundę doInBackground()
. AsyncTasks używają wewnętrznie kolejki o stałym rozmiarze do przechowywania opóźnionych zadań. Rozmiar kolejki to domyślnie 10. Jeśli rozpoczniesz 15 niestandardowych zadań z rzędu, pierwsze 5 wprowadzi je doInBackground()
, ale reszta będzie czekać w kolejce na wolny wątek roboczy. Gdy tylko jedna z pierwszych 5 zakończy się, a tym samym zwolni wątek roboczy, zadanie z kolejki rozpocznie wykonywanie. Tak więc w tym przypadku jednocześnie uruchomi się maksymalnie 5 zadań. Jeśli jednak uruchomisz 16 niestandardowych zadań z rzędu, pierwsze 5 wprowadzi je doInBackground()
, a pozostałe 10 dostanie się do kolejki, ale dla 16. zostanie utworzony nowy wątek roboczy, aby natychmiast rozpocząć wykonywanie. Tak więc w tym przypadku jednocześnie uruchomi się maksymalnie 6 zadań.
Istnieje limit liczby zadań, które można uruchomić jednocześnie. Ponieważ AsyncTask
używa modułu wykonującego pulę wątków z ograniczoną maksymalną liczbą wątków roboczych (128), a kolejka zadań opóźnionych ma ustalony rozmiar 10, próba wykonania więcej niż 138 niestandardowych zadań spowoduje awarię aplikacji java.util.concurrent.RejectedExecutionException
.
Począwszy od wersji 3.0 interfejs API pozwala na użycie niestandardowego modułu wykonującego pulę wątków za pomocą AsyncTask.executeOnExecutor(Executor exec, Params... params)
metody. Pozwala to na przykład skonfigurować rozmiar kolejki opóźnionych zadań, jeśli domyślna wartość 10 nie jest potrzebna.
Jak wspomina @Knossos, istnieje możliwość korzystania AsyncTaskCompat.executeParallel(task, params);
z biblioteki wsparcia v.4 do równoległego uruchamiania zadań bez zawracania sobie głowy poziomem API. Ta metoda stała się przestarzała na poziomie interfejsu API 26.0.0.
AKTUALIZACJA: 3
Oto prosta aplikacja testowa do grania z wieloma zadaniami, wykonywanie szeregowe vs. równoległe: https://github.com/vitkhudenko/test_asynctask
AKTUALIZACJA: 4 (dzięki @penkzhou za zwrócenie na to uwagi)
Począwszy od Androida 4.4 AsyncTask
zachowuje się inaczej niż opisano w sekcji AKTUALIZACJA: 2 . Istnieje poprawka zapobiegająca AsyncTask
tworzeniu zbyt wielu wątków.
Przed Androidem 4.4 (API 19) AsyncTask
miał następujące pola:
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);
W Androidzie 4.4 (API 19) powyższe pola zostały zmienione na:
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);
Ta zmiana zwiększa rozmiar kolejki do 128 elementów i zmniejsza maksymalną liczbę wątków do liczby rdzeni procesora * 2 + 1. Aplikacje mogą nadal przesyłać tę samą liczbę zadań.