Android AsyncTask 스레드 제한?


95

사용자가 시스템에 로그인 할 때마다 일부 정보를 업데이트해야하는 응용 프로그램을 개발 중이며 전화에서도 데이터베이스를 사용합니다. 모든 작업 (업데이트, 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");
    }
}

답변:


207

모든 AsyncTask는 공유 (정적) ThreadPoolExecutorLinkedBlockingQueue에 의해 내부적으로 제어됩니다 . executeAsyncTask 를 호출하면 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은 여전히 ​​대기열에 있습니다.


2
"이것은 장기 실행 작업에 AsyncTasks를 사용해서는 안되는 매우 좋은 이유입니다."이 시나리오에 대한 권장 사항은 무엇입니까? 새 스레드를 수동으로 생성하거나 자체 실행기 서비스를 생성합니까?
user123321

2
실행자는 기본적으로 스레드 위에 추상화되어 있으므로이를 관리하기 위해 복잡한 코드를 작성할 필요가 없습니다. 작업을 실행하는 방법에서 분리합니다. 코드가 실행자에만 의존하는 경우 사용되는 스레드 수 등을 투명하게 변경하는 것이 쉽습니다. 간단한 작업의 경우에도 작업량이 집행자는 적지 않더라도 동일합니다.
antonyt 2012-08-09

37
Android 3.0 이상에서는 동시 AsyncTask의 기본 수가 1 개로 감소했습니다. 추가 정보 : developer.android.com/reference/android/os/…
Kieran

와, 훌륭한 답변에 감사드립니다. 마지막으로 내 코드가 산발적으로 그리고 신비하게 실패하는 이유에 대한 설명이 있습니다.
Chris Knight

@antonyt, 한 가지 더 의심, Canceled AsyncTasks, AsyncTasks 수에 포함됩니까? 즉, core pool size또는 maximum pool size?
nmxprime

9

@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 에서 확인하세요 .


마지막은 :( 오년 전 커밋
LukaszTaraszka

5

업데이트 : 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)의 모든 인스턴스에서 공유된다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.