사용자가 시스템에 로그인 할 때마다 일부 정보를 업데이트해야하는 응용 프로그램을 개발 중이며 전화에서도 데이터베이스를 사용합니다. 모든 작업 (업데이트, db에서 데이터 검색 등)에 대해 비동기 작업을 사용합니다. 지금까지는 왜 사용하지 말아야하는지 알지 못했지만 최근에 일부 작업을 수행하면 일부 비동기 작업이 사전 실행에서 중지되고 doInBackground로 이동하지 않는 것을 경험했습니다. 그렇게 놔두기에는 너무 이상했기 때문에 문제를 확인하기 위해 또 다른 간단한 응용 프로그램을 개발했습니다. 이상하게도 총 비동기 작업 수가 5 개에 도달하면 동일한 동작이 발생하고 6 번째 작업은 사전 실행에서 중지됩니다.
Android에는 활동 / 앱에 대한 asyncTasks 제한이 있습니까? 아니면 버그 일 뿐이며보고해야합니까? 누구든지 동일한 문제를 경험하고 아마도 해결 방법을 찾았습니까?
다음은 코드입니다.
백그라운드에서 작동하도록 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");
}
}
답변
모든 AsyncTask는 공유 (정적) ThreadPoolExecutor 및 LinkedBlockingQueue에 의해 내부적으로 제어됩니다 . execute
AsyncTask 를 호출하면 ThreadPoolExecutor
은 (는) 나중에 준비가되면 실행합니다.
‘언제 준비 됐어?’ 의 동작은 코어 풀 크기 와 최대 풀 크기의ThreadPoolExecutor
두 매개 변수에 의해 제어됩니다 . 현재 활성 상태 인 코어 풀 크기 미만의 스레드가 있고 새 작업이 들어 오면 실행기는 새 스레드를 만들고 즉시 실행합니다. 최소한 코어 풀 크기 스레드가 실행 중이면 작업을 큐에 넣고 사용 가능한 유휴 스레드가있을 때까지 (즉, 다른 작업이 완료 될 때까지) 대기합니다. 작업을 대기열에 넣을 수없는 경우 (대기열은 최대 용량을 가질 수 있음) 작업을 실행할 새 스레드 (최대 풀 크기 스레드까지)를 생성합니다. 코어가 아닌 유휴 스레드는 결국 해제 될 수 있습니다. 연결 유지 제한 시간 매개 변수에 따라.
Android 1.6 이전에는 코어 풀 크기가 1이고 최대 풀 크기가 10이었습니다. Android 1.6 이후 코어 풀 크기는 5이고 최대 풀 크기는 128입니다. 두 경우 모두 대기열 크기는 10입니다. 연결 유지 시간 제한은 2.3 이전에는 10 초 였고 그 이후로는 1 초였습니다.
이 모든 것을 염두에두고에서 AsyncTask
작업의 5/6 만 실행하는 것처럼 보이는 이유가 명확 해졌습니다 . 6 번째 작업은 다른 작업 중 하나가 완료 될 때까지 대기열에 추가됩니다. 이것이 장기 실행 작업에 AsyncTasks를 사용해서는 안되는 매우 좋은 이유입니다. 다른 AsyncTask가 실행되지 않도록 방지합니다.
완전성을 위해 6 개 이상의 작업 (예 : 30 개)으로 운동을 반복 doInBackground
하면 대기열이 가득 차고 실행기가 더 많은 작업자 스레드를 생성하도록 푸시됨에 따라 6 개 이상이 입력 되는 것을 볼 수 있습니다. 장기 실행 작업을 계속하면 20/30이 활성화되고 10은 여전히 대기열에 있습니다.
답변
@antonyt가 정답을 가지고 있지만 간단한 해결책을 찾고 있다면 Needle을 확인할 수 있습니다.
이를 통해 사용자 지정 스레드 풀 크기를 정의 할 수 있으며.와 달리 모든 Android 버전에서 동일 AsyncTask
하게 작동합니다 . 그것으로 다음과 같이 말할 수 있습니다.
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 에서 확인하세요 .
답변
업데이트 : CPU * 2 +1의 최대로 성장하면서 API 19 코어 스레드 풀 크기는 시작에 4의 2의 최소 및 최대 장치의 CPU 수를 반영하도록 변경 되었기 때문에 – 참조
// 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)
작업을 실행할 Executor를 제공 할 수 있습니다. 내부 실행기에 THREAD_POOL_EXECUTOR를 제공 할 수 있지만 작업을 직렬화하지 않거나 자체 실행기를 만들어 여기에 제공 할 수도 있습니다. 그러나 Javadocs의 경고에주의하십시오.
경고 : 여러 작업이 스레드 풀에서 병렬로 실행되도록 허용하는 것은 작업 순서가 정의되지 않았기 때문에 일반적으로 원하는 작업이 아닙니다. 예를 들어 이러한 작업을 사용하여 공통 상태를 수정하는 경우 (예 : 단추 클릭으로 인한 파일 쓰기) 수정 순서에 대한 보장이 없습니다. 주의 깊게 작업하지 않으면 드물게 최신 버전의 데이터를 이전 버전으로 덮어 쓰게되어 모호한 데이터 손실 및 안정성 문제가 발생할 수 있습니다. 이러한 변경은 직렬로 실행하는 것이 가장 좋습니다. 이러한 작업이 플랫폼 버전에 관계없이 직렬화되도록 보장하기 위해이 함수를 SERIAL_EXECUTOR와 함께 사용할 수 있습니다.
한 가지 더 주목할 점은 프레임 워크에서 제공하는 Executors THREAD_POOL_EXECUTOR 및 직렬 버전 SERIAL_EXECUTOR (AsyncTask의 기본값)는 모두 정적 (클래스 수준 구성)이므로 앱 프로세스 전체에서 AsyncTask (s)의 모든 인스턴스에서 공유된다는 것입니다.