Android AsyncTask threads limits?
Rozwijam aplikację, w której muszę aktualizować niektóre informacje za każdym razem, gdy użytkownik loguje się do systemu, korzystam również z bazy danych w telefonie. Dla wszystkich tych operacji (aktualizacje, pobieranie danych z db itd.) Używam zadań asynchronicznych. Jak do tej pory nie widziałem, dlaczego nie powinienem ich używać, ale ostatnio doświadczyłem, że jeśli wykonam kilka operacji, niektóre z moich zadań asynchronicznych po prostu zatrzymują się na pre-execute i nie przeskakują do doInBackground. To było zbyt dziwne, żeby to tak zostawić, więc rozwinąłem kolejna prosta aplikacja, aby sprawdzić, co jest nie tak. I dość dziwne, mam to samo zachowanie, gdy liczba wszystkich zadań asynchronicznych osiągnie 5, szósty zatrzymuje się na Pre-execute.
Czy android ma limit asynchronicznych zadań na aktywności / aplikacji? A może to tylko jakiś bug i należy go zgłosić? Czy ktoś doświadczył tego samego problemu i może znalazł obejście tego problemu?
Oto kod:
Po prostu utwórz 5 z tych wątków, aby działały w tle:
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");
}
}
A następnie utworzyć ten wątek. Wejdzie w preExecute i zawiśnie (nie przejdzie do 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");
}
}
3 answers
Wszystkie asynchroniczne zadania są kontrolowane wewnętrznie przez wspólny (statyczny) ThreadPoolExecutor i LinkedBlockingQueue. Gdy wywołasz execute
Na Asynctasku, ThreadPoolExecutor
wykona ją, gdy będzie gotowa w przyszłości.
Przed Androidem 1.6 rozmiar puli rdzenia wynosił 1, A Maksymalny rozmiar puli wynosił 10. Od Androida 1.6 rozmiar puli rdzenia wynosi 5, a maksymalny rozmiar Puli wynosi 128. Wielkość kolejki w obu przypadkach wynosi 10. Czas utrzymywania przy życiu wynosił 10 sekund przed 2.3 i 1 sekundę od tego czasu.
Mając to wszystko na uwadze, teraz staje się jasne, dlaczego AsyncTask
pojawi się tylko do wykonania 5/6 Twoich zadań. Szóste zadanie jest ustawiane w kolejce do ukończenia jednego z pozostałych zadań. To jest bardzo dobry powód, dla którego nie powinieneś używać asynchronicznych zadań do długotrwałych operacji - uniemożliwi to działanie innych asynchronicznych Zadań.
Dla kompletności, jeśli powtórzysz ćwiczenie z więcej niż 6 zadaniami (np. 30), zobaczysz, że więcej niż 6 wejdzie doInBackground
, gdy kolejka stanie się pełna i executor zostanie wypchnięty, aby utworzyć więcej wątków roboczych. Jeśli utrzymałeś zadanie długo działające, powinieneś zobaczyć, że 20/30 staje się aktywne, a 10 nadal jest w kolejce.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-06 18:02:14
@antonit ma prawidłową odpowiedź, ale jeśli szukasz prostego rozwiązania, możesz sprawdzić igłę.
Za jego pomocą można zdefiniować niestandardowy rozmiar puli wątków i, w przeciwieństwie do AsyncTask
, działa on na wszystkich wersjach Androida tak samo. Z nim można powiedzieć takie rzeczy jak:
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);
}
});
Lub rzeczy podobne
Needle.onMainThread().execute(new Runnable() {
@Override
public void run() {
// e.g. change one of the views
}
});
Może zrobić nawet o wiele więcej. Sprawdź to na GitHub.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 23:11:24
Update: od API 19 rozmiar puli wątków rdzenia został zmieniony, aby odzwierciedlić liczbę procesorów na urządzeniu, z minimum 2 i maksymalnie 4 na początku, przy jednoczesnym wzroście do maksymalnej liczby procesorów*2 +1 - odniesienie
// 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;
Należy również zauważyć, że podczas gdy domyślny wykonawca AsyncTask jest szeregowy (wykonuje jedno zadanie na raz i w kolejności, w jakiej przybywa), za pomocą metody
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params)
Możesz podać Wykonawcę do wykonywania swoich zadań. Możesz podać THREAD_POOL_EXECUTOR pod maską executor, ale bez serializacji zadań, lub możesz nawet utworzyć własny Executor i dostarczyć go tutaj. Zwróć jednak uwagę na ostrzeżenie w Javadocs.
Ostrzeżenie: pozwolenie na równoległe uruchamianie wielu zadań z puli wątków nie jest na ogół tym, czego się chce, ponieważ kolejność ich działania nie jest określona. Na przykład, jeśli te zadania są używane do modyfikowania dowolnego wspólnego stanu (takiego jak zapisanie pliku za pomocą kliknięcia przycisku), tam nie gwarantują kolejności modyfikacji. Bez starannej pracy w rzadkich przypadkach możliwe jest, że nowsza wersja danych zostanie nadpisana przez starszą, co prowadzi do niejasnych problemów z utratą danych i stabilnością. Takie zmiany są najlepiej wykonywane w trybie szeregowym; aby zagwarantować, że taka praca jest serializowana niezależnie od wersji platformy, możesz użyć tej funkcji z SERIAL_EXECUTOR.
Jeszcze jedną rzeczą do zauważenia jest to, że zarówno Framework dostarczał Executorów THREAD_POOL_EXECUTOR, jak i jego wersja szeregowa SERIAL_EXECUTOR (która jest domyślna dla AsyncTask) są statyczne(konstrukcje na poziomie klasy) i dlatego są współdzielone przez wszystkie instancje AsyncTask w procesie aplikacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-26 22:21:44