여러 개의 AsyncTask를 동시에 실행하는 것은 불가능합니까?


259

두 개의 AsyncTask를 동시에 실행하려고합니다. (플랫폼은 Android 1.5, HTC Hero입니다.) 그러나 첫 번째 만 실행됩니다. 다음은 내 문제를 설명하는 간단한 스 니펫입니다.

public class AndroidJunk extends Activity {
 class PrinterTask extends AsyncTask<String, Void, Void> {
     protected Void doInBackground(String ... x) {
      while (true) {
       System.out.println(x[0]);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException ie) {
        ie.printStackTrace();
       }
      }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new PrinterTask().execute("bar bar bar");
        new PrinterTask().execute("foo foo foo");

        System.out.println("onCreate() is done.");
    }
}

내가 기대하는 결과는 다음과 같습니다.

onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo

등등. 그러나 내가 얻는 것은 다음과 같습니다.

onCreate() is done.
bar bar bar
bar bar bar
bar bar bar

두 번째 AsyncTask는 실행되지 않습니다. execute () 문의 순서를 변경하면 foo 작업 만 출력을 생성합니다.

여기에 명백한 것이 누락되었거나 어리석은 일이 있습니까? 두 개의 AsyncTask를 동시에 실행할 수 없습니까?

편집 : 문제의 전화가 Android 1.5를 실행한다는 것을 깨달았습니다. 문제 설명을 업데이트했습니다. 따라서. Android 2.1을 실행하는 HTC Hero에는이 문제가 없습니다. 흠 ...


귀하의 코드가 저에게 효과적이므로 문제는 다른 곳에 있어야합니다. LogCat보기에서 필터를 입력 했습니까? ;-)
mreichelt

흠, 이상하다. logcat에 필터링이 없습니다. 1.6도 사용하고 있습니까? 그렇다면 어떤 전화입니까?
rodion

죄송합니다. Android 1.5를 실행 중임을 깨달았습니다.
rodion

1
Android 1.6을 대상으로 사용하고 Android 2.1 에뮬레이터를 사용했습니다. 따라서 Android 1.5가있는 HTC Hero에서만 문제가 발생하면 나사로 고정하십시오. ;-) HTC Hero는 이미 최신 Android 버전으로 업데이트되었습니다. 물건을 망치는 제조업체가 있다면 신경 쓰지 않을 것입니다. 또한 Android 1.5에 대해서는 더 이상 신경 쓰지 않습니다.
mreichelt

AsyncTask는 5ms의 짧은 지속 시간 작업에 사용해야합니다. ThreadPoolExecutor ( developer.android.com/reference/java/util/concurrent/… )로 이동하십시오 . 관련 게시물 : stackoverflow.com/questions/6964011/…
Ravindra babu

답변:


438

AsyncTask는 doInBackground ()에서 항목을 실행하기 위해 스레드 풀 패턴을 사용합니다. 이 문제는 초기에 (초기 Android OS 버전에서) 풀 크기가 1에 불과하여 많은 AsyncTask에 대한 병렬 계산이 없음을 의미합니다. 그러나 나중에 그들은 그 크기를 5로 고쳤으므로 최대 5 개의 AsyncTasks가 동시에 실행될 수 있습니다. 불행히도 나는 그들이 어떤 버전으로 정확하게 변경했는지 기억하지 못합니다.

최신 정보:

현재 (2012-01-27) API가 이에 대해 말한 내용은 다음과 같습니다.

처음 소개되었을 때 AsyncTasks는 단일 백그라운드 스레드에서 순차적으로 실행되었습니다. DONUT부터는 여러 작업을 병렬로 수행 할 수있는 스레드 풀로 변경되었습니다. HONEYCOMB 후에는 병렬 실행으로 인한 일반적인 응용 프로그램 오류를 피하기 위해이를 단일 스레드로 다시 변경할 계획입니다. 병렬 실행을 원한다면 THREAD_POOL_EXECUTOR와 함께이 메소드의 executeOnExecutor (Executor, Params ...) 버전을 사용할 수 있습니다. 그러나 사용에 대한 경고는 주석을 참조하십시오.

DONUT는 Android 1.6이고 HONEYCOMB은 Android 3.0입니다.

업데이트 : 2

에 의해 코멘트보기 kabuko에서을 Mar 7 2012 at 1:27.

"여러 작업을 병렬로 실행할 수있는 스레드 풀"이 사용되는 API (1.6에서 시작하여 3.0으로 끝남)가 동시에 실행되는 AsyncTask의 수는 이미 실행을 위해 전달 된 작업 수에 따라 다릅니다. 그들의 완료되지 않은 doInBackground()아직.

이것은 2.2에서 나에 의해 테스트 / 확인되었습니다. 에 잠 들어있는 사용자 지정 AsyncTask가 있다고 가정합니다 doInBackground(). AsyncTask는 지연된 작업을 저장하기 위해 내부적으로 고정 크기 대기열을 사용합니다. 대기열 크기는 기본적으로 10입니다. 15 개의 사용자 지정 작업을 연속으로 시작하면 처음 5 개는에 입력 doInBackground()하지만 나머지는 대기열에서 비어있는 작업자 스레드를 기다립니다. 처음 5 개 중 하나가 완료되고 작업자 스레드를 해제하면 대기열의 작업이 실행을 시작합니다. 따라서이 경우 최대 5 개의 작업이 동시에 실행됩니다. 그러나 16 개의 사용자 지정 작업을 연속으로 시작하면 처음 5 개가로 들어가고 doInBackground()나머지 10 개가 대기열에 들어가지만 16 일 동안 새 작업자 스레드가 만들어져 즉시 실행을 시작합니다. 따라서이 경우 최대 6 개의 작업이 동시에 실행됩니다.

동시에 실행할 수있는 작업 수에는 제한이 있습니다. 이후 AsyncTask사용하는 작업자 스레드 (128) 및 당신이 충돌합니다 이상 138 사용자 정의 작업 응용 프로그램을 실행하려고하면 큐, 크기 10 고정했다 지연된 작업의 제한 최대 수의 스레드 풀 실행자 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부터는 UPDATE : 2 섹션 AsyncTask에서 설명한 것과 다르게 작동 합니다. 너무 많은 스레드를 작성 하지 못하게 하는 수정 사항 이 있습니다 .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로 줄어 듭니다. 앱은 여전히 ​​동일한 수의 작업을 제출할 수 있습니다.


11
참고로 코어 풀 크기는 5이지만 최대 값은 128입니다. 이는 큐를 채우면 5 개 (최대 128 개)를 동시에 실행할 수 있음을 의미합니다.
kabuko

2
@kabuko : 당신은 절대적으로 맞습니다. 2.2에서 이것을 조사하고 5 개의 작업이 이미 실행 중이면 (코어 풀 크기가 5 인 경우) 나머지 작업을 지연 된 작업의 대기열에 넣기 시작하지만 대기열이 이미 가득 찬 경우 (최대 대기열 크기는 10 임) ), 작업에 대한 추가 작업자 스레드를 시작하므로 작업이 즉시 시작됩니다.
Vit Khudenko

1
그냥 내 일을 저장, 자막 영웅 : 당신 감사
카 롤리

2
새로운 compat 기능 을 위해이 답변을 업데이트하십시오 . 예 :AsyncTaskCompat.executeParallel(task, params);
Knossos

1
이후, 답을 업데이트하십시오 @Arhimed AsyncTaskCompat.executeParallel(task, params);지금은 사용되지 않는
키릴 Starostin

218

이를 통해 API 4 이상 (Android 1.6 이상)을 사용하는 모든 Android 버전에서 병렬 실행이 가능합니다.

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}

Arhimed의 탁월한 답변을 요약 한 것입니다.

프로젝트 빌드 대상으로 API 레벨 11 이상을 사용하십시오. 이클립스에서 그것은입니다 Project > Properties > Android > Project Build Target. 더 낮은 API 수준과의 호환성 은 유지 하지 않습니다 . 이후에 소개 된 기능을 실수로 사용하면 Lint 오류가 발생하므로 걱정하지 마십시오 minSdkVersion. 이후에 소개 된 기능을 실제로 사용하려면minSdkVersion , 당신은 주석을 사용하여 이러한 오류를 억제 할 수 있지만,이 경우에, 당신은 호환성에 대해 돌볼 필요가 자신을 . 이것은 위의 코드 스 니펫에서 정확히 일어난 일입니다.


3
TimerTask.THREAD_POOL_EXECUTOR를 AsynTask.THREAD_POOL_EXECUTOR로 변경했다면이 예제가 작동합니다
Ronnie

2
이슈를 무효화하기 위해 호출을 일반화 할 수 있습니다 : void startMyTask(AsyncTask<Void, ?, ?> asyncTask) (doInBackground가 무효 인 경우)
Peter

클래스가 AsyncTask <Void, Integer, Void>를 확장하면 위의 코드를 훨씬 더 단순화 할 수 있습니다. 감사합니다.
피터 Arandorenko

1
고마워 웹 서버에서 데이터를 읽기 위해 두 개의 비동기 작업을 동시에 실행 해야하는 시나리오가 있었지만 서버는 하나의 URL 만 수신하고 특정 요청이 완료되지 않는 한 두 번째 AsyncTask의 URL이 서버에 충돌하지 않습니다. . 당신의 대답은 내 문제 : 해결
Santhosh 구타

1
나는 이것에 대한 공감대를 계속하고 있습니다. 감사합니다. :) 그러나 많은 사람들이 Volley , Glide 또는 Swagger 와 같은 우수한 라이브러리 가 더 나은 선택 인 네트워킹에 AsyncTask를 사용하려고한다는 인상을 받았습니다 . AsyncTask를 사용하기 전에 다음을 확인하십시오 :)
sulai

21

@sulai 제안을보다 일반적인 것으로 만들기 :

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> asyncTask, T... params) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}   

더 일반적인 해결책 : AsyncTaskCompat.executeParallel (new MyAsyncTask, myprams);
Vikash Kumar Verma

11

@sulai의 아주 좋은 요약에서 @Arhimed의 뽀얀 답변에 최신 업데이트 (UPDATE 4)를 포함 시키십시오.

void doTheTask(AsyncTask task) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 (API 19) and above
        // Parallel AsyncTasks are possible, with the thread-pool size dependent on device
        // hardware
        task.execute(params);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Android 3.0 to
        // Android 4.3
        // Parallel AsyncTasks are not possible unless using executeOnExecutor
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    } else { // Below Android 3.0
        // Parallel AsyncTasks are possible, with fixed thread-pool size
        task.execute(params);
    }
}

오, 대박. 이것은 .executeOnExecutor()이제 API> = KITKAT에 대해 중복 됨을 의미합니다 .
Taslim Oseni


3

가능하다. 내 안드로이드 장치 버전은 4.0.4이고 android.os.Build.VERSION.SDK_INT는 15입니다

나는 3 스피너가

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

또한 Async-Tack 클래스가 있습니다.

여기 내 스피너 로딩 코드가 있습니다.

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();

RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();

RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

이것은 완벽하게 작동합니다. 내 스피너가 하나씩로드되었습니다. 나는 executeOnExecutor를 사용하지 않았다.

여기 내 비동기 작업 클래스가 있습니다.

public class RequestSend  extends AsyncTask<String, String, String > {

    private ProgressDialog dialog = null;
    public Spinner spin;
    public String where;
    public String title;
    Context con;
    Activity activity;      
    String[] items;

    public RequestSend(Context activityContext) {
        con = activityContext;
        dialog = new ProgressDialog(activityContext);
        this.activity = activityContext;
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);       
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spin.setAdapter(adapter);
        } catch (NullPointerException e) {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        } catch (Exception e)  {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
        super.onPostExecute(result);

        if (dialog != null)
            dialog.dismiss();   
    }

    protected void onPreExecute() {
        super.onPreExecute();
        dialog.setTitle(title);
        dialog.setMessage("Wait...");
        dialog.setCancelable(false); 
        dialog.show();
    }

    @Override
    protected String doInBackground(String... Strings) {
        try {
            Send_Request();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    public void Send_Request() throws JSONException {

        try {
            String DataSendingTo = "http://www.example.com/AppRequest/" + where;
            //HttpClient
            HttpClient httpClient = new DefaultHttpClient();
            //Post header
            HttpPost httpPost = new HttpPost(DataSendingTo);
            //Adding data
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

            nameValuePairs.add(new BasicNameValuePair("authorized","001"));

            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // execute HTTP post request
            HttpResponse response = httpClient.execute(httpPost);

            BufferedReader reader;
            try {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder builder = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    builder.append(line) ;
                }

                JSONTokener tokener = new JSONTokener(builder.toString());
                JSONArray finalResult = new JSONArray(tokener);
                items = new String[finalResult.length()]; 
                // looping through All details and store in public String array
                for(int i = 0; i < finalResult.length(); i++) {
                    JSONObject c = finalResult.getJSONObject(i);
                    items[i]=c.getString("data_name");
                }

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2
그것들이 순차적으로 실행되지 않았다는 것을 어떻게 알 수 있습니까?
behelit

@behelit 작동 할 때 중요합니까? 자세한 내용은 허용되는 답변을 읽으십시오. stackoverflow.com/a/4072832/2345900
Sajitha Rathnayake

2
이해가 안되서 죄송합니다. 비동기가 순차적인지 아니면 실제로 비동기인지 알 수 없다면 중요합니다. 또한 링크 된 답변은 실제로 응답에서 사용하지 않는 executeonexecutor 메소드를 사용한다고 말합니다. 내가 뭔가를 놓친다면 죄송합니다.
behelit

1
"나의 스피너가 하나씩로드되었습니다." 당신은 말 그대로 이것이 병렬 실행이 아니라 직렬 실행이라고 직접 말했습니다. 각각의 AsyncTask는 대기열에 추가되고 순차적으로 처리됩니다. 이것은 OP의 질문에 대답하지 않습니다.
Joshua Pinter

이것은 매우 오래된 답변입니다. 문제가 해결되지 않으면 당신은 허용 대답을 참조하시기 바랍니다
Sajitha Rathnayake

3

작업을 병렬로 실행하려면 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")Android 버전 3.0 이후 에 메소드를 호출해야합니다 . 그러나이 메소드는 자체적으로 병렬로 실행되기 때문에 Android 3.0 이전 및 1.6 이후에는 존재하지 않으므로 다른 Android 버전에서 예외를 발생시키지 않도록 프로젝트에서 자체 AsyncTask 클래스를 사용자 정의하는 것이 좋습니다.

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