Chạy nhiều AsyncT task cùng một lúc - không thể?


259

Tôi đang cố gắng chạy hai AsyncT task cùng một lúc. (Nền tảng là Android 1.5, HTC Hero.) Tuy nhiên, chỉ có cái đầu tiên được thực thi. Đây là một đoạn đơn giản để mô tả vấn đề của tôi:

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.");
    }
}

Đầu ra tôi mong đợi là:

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

Và như thế. Tuy nhiên, những gì tôi nhận được là:

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

AsyncTask thứ hai không bao giờ được thực thi. Nếu tôi thay đổi thứ tự của câu lệnh exec (), chỉ tác vụ foo sẽ tạo đầu ra.

Tôi có thiếu một cái gì đó rõ ràng ở đây và / hoặc làm điều gì đó ngu ngốc? Có phải là không thể chạy hai AsyncT task cùng một lúc không?

Chỉnh sửa: Tôi nhận ra điện thoại trong câu hỏi chạy Android 1.5, tôi đã cập nhật vấn đề descr. phù hợp. Tôi không gặp vấn đề này với HTC Hero chạy Android 2.1. Ừm ...


Mã của bạn làm việc cho tôi, vì vậy vấn đề phải ở một nơi khác. Bạn đã nhập bộ lọc trong chế độ xem LogCat của mình chưa? ;-)
mreichelt

Hừm, thật lạ. Tôi không có bất kỳ bộ lọc trong logcat. Bạn có đang sử dụng 1.6 không? Nếu vậy, điện thoại nào?
Rodion

Rất tiếc, chỉ cần nhận ra rằng nó đang chạy (cổ) ​​Android 1.5
Rodion

1
Tôi đã sử dụng Android 1.6 làm mục tiêu và trình giả lập Android 2.1. Vì vậy, nếu sự cố thực sự xảy ra trên HTC Hero chỉ với Android 1.5 - hãy vặn chúng, bạn vẫn ổn. ;-) HTC Hero đã có bản cập nhật cho phiên bản Android mới hơn. Tôi sẽ không bận tâm về nó nếu có một số nhà sản xuất làm hỏng mọi thứ. Ngoài ra, tôi sẽ không quan tâm đến Android 1.5 nữa.
mreichelt

AsyncTask nên được sử dụng cho các nhiệm vụ thời gian ngắn hơn 5 ms. Di chuyển đến ThreadPoolExecutor ( developer.android.com/reference/java/util/concản/iêu ). Bài đăng liên quan: stackoverflow.com/questions/6964011/
Mạnh

Câu trả lời:


438

AsyncTask sử dụng mẫu nhóm luồng để chạy nội dung từ doInBackground (). Vấn đề ban đầu (trong các phiên bản HĐH Android đầu tiên) kích thước nhóm chỉ là 1, nghĩa là không có tính toán song song cho một loạt các AsyncT Nhiệm vụ. Nhưng sau đó họ đã sửa nó và bây giờ kích thước là 5, vì vậy nhiều nhất là 5 AsyncT task có thể chạy đồng thời. Thật không may, tôi không nhớ chính xác phiên bản nào họ đã thay đổi điều đó.

CẬP NHẬT:

Đây là những gì API hiện tại (2012-01-27) nói về điều này:

Khi được giới thiệu lần đầu tiên, AsyncT task đã được thực thi ser seri trên một luồng nền đơn. Bắt đầu với DONUT, điều này đã được thay đổi thành một nhóm các luồng cho phép nhiều tác vụ hoạt động song song. Sau HONEYCOMB, nó được lên kế hoạch để thay đổi điều này trở lại thành một luồng duy nhất để tránh các lỗi ứng dụng phổ biến do thực thi song song. Nếu bạn thực sự muốn thực thi song song, bạn có thể sử dụng phiên bản execOnExecutor (Executor, Params ...) của phương thức này với THREAD_POOL_EXECUTOR; tuy nhiên, xem bình luận ở đó để cảnh báo về việc sử dụng nó.

DONUT là Android 1.6, HONEYCOMB là Android 3.0.

CẬP NHẬT: 2

Xem bình luận của kabukotừ Mar 7 2012 at 1:27.

Nó chỉ ra rằng đối với các API trong đó "một nhóm các luồng cho phép nhiều tác vụ hoạt động song song" được sử dụng (bắt đầu từ 1.6 và kết thúc vào 3.0), số lượng AsyncT task chạy đồng thời phụ thuộc vào số lượng tác vụ đã được chuyển để thực thi, nhưng chưa hoàn thành của họ doInBackground()được nêu ra.

Điều này đã được thử nghiệm / xác nhận bởi tôi vào ngày 2.2. Giả sử bạn có một AsyncTask tùy chỉnh chỉ cần ngủ một giây doInBackground(). AsyncT task sử dụng hàng đợi kích thước cố định trong nội bộ để lưu trữ các tác vụ bị trì hoãn. Kích thước hàng đợi là 10 theo mặc định. Nếu bạn bắt đầu 15 tác vụ tùy chỉnh của mình liên tiếp, thì 5 đầu tiên sẽ nhập chúng doInBackground(), nhưng phần còn lại sẽ đợi trong hàng đợi cho một luồng công nhân miễn phí. Ngay khi bất kỳ trong số 5 kết thúc đầu tiên kết thúc và do đó giải phóng một luồng công nhân, một tác vụ từ hàng đợi sẽ bắt đầu thực thi. Vì vậy, trong trường hợp này, tối đa 5 nhiệm vụ sẽ chạy đồng thời. Tuy nhiên, nếu bạn bắt đầu 16 tác vụ tùy chỉnh liên tiếp, thì 5 đầu tiên sẽ nhập chúng doInBackground(), 10 phần còn lại sẽ vào hàng đợi, nhưng trong lần thứ 16, một luồng công nhân mới sẽ được tạo để nó bắt đầu thực thi ngay lập tức. Vì vậy, trong trường hợp này, nhiều nhất là 6 nhiệm vụ sẽ chạy đồng thời.

Có giới hạn về số lượng tác vụ có thể được chạy đồng thời. Do AsyncTasksử dụng bộ thực thi nhóm luồng với số lượng luồng công nhân tối đa hạn chế (128) và hàng đợi tác vụ bị trì hoãn có kích thước 10 cố định, nếu bạn cố thực hiện hơn 138 tác vụ tùy chỉnh của mình, ứng dụng sẽ gặp sự cố java.util.concurrent.RejectedExecutionException.

Bắt đầu từ 3.0, API cho phép sử dụng trình thực thi nhóm luồng tùy chỉnh của bạn thông qua AsyncTask.executeOnExecutor(Executor exec, Params... params)phương thức. Ví dụ, điều này cho phép định cấu hình kích thước của hàng đợi tác vụ bị trì hoãn nếu mặc định 10 không phải là thứ bạn cần.

Như @Knossos đề cập, có một tùy chọn để sử dụng AsyncTaskCompat.executeParallel(task, params);từ thư viện v.4 hỗ trợ để chạy các tác vụ song song mà không bận tâm đến mức API. Phương pháp này trở nên không dùng nữa trong API cấp 26.0.0.

CẬP NHẬT: 3

Đây là một ứng dụng thử nghiệm đơn giản để chơi với số lượng tác vụ, thực hiện nối tiếp so với thực thi song song: https://github.com/vitkhudenko/test_asynctask

CẬP NHẬT: 4 (cảm ơn @penkzhou đã chỉ ra điều này)

Bắt đầu từ Android 4.4 AsyncTaskhoạt động khác với những gì được mô tả trong phần CẬP NHẬT: 2 . Có một sửa chữa để ngăn chặn AsyncTaskquá nhiều chủ đề.

Trước Android 4.4 (API 19) AsyncTaskđã có các trường sau:

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);

Trong Android 4.4 (API 19), các trường trên được thay đổi thành:

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);

Thay đổi này làm tăng kích thước của hàng đợi lên 128 mục và giảm số lượng luồng tối đa xuống số lõi CPU * 2 + 1. Ứng dụng vẫn có thể gửi cùng số lượng tác vụ.


11
Chỉ cần FYI, kích thước nhóm lõi là 5, nhưng tối đa là 128. Điều này có nghĩa là nếu bạn điền vào hàng đợi của mình, hơn 5 (tối đa 128) có thể chạy đồng thời.
kabuko

2
@kabuko: bạn hoàn toàn đúng. Chỉ cần điều tra điều này trên 2.2 và xác nhận rằng nếu 5 tác vụ đã chạy (kích thước nhóm lõi là 5), thì nó bắt đầu đưa các nhiệm vụ còn lại vào hàng đợi của các tác vụ bị trì hoãn, tuy nhiên nếu hàng đợi đã đầy (kích thước hàng đợi tối đa là 10 ), sau đó nó bắt đầu một luồng công nhân bổ sung cho tác vụ để tác vụ bắt đầu ngay lập tức.
Vit Khudenko

1
vừa cứu tôi, anh hùng chú thích :) Cảm ơn bạn
Karoly

2
Vui lòng cập nhật câu trả lời này cho các chức năng compat mới . Ví dụ:AsyncTaskCompat.executeParallel(task, params);
Knossos

1
@Arhimed Xin vui lòng, cập nhật câu trả lời vì AsyncTaskCompat.executeParallel(task, params);hiện không được chấp nhận
Kirill Starostin

218

Điều này cho phép thực thi song song trên tất cả các phiên bản Android có API 4+ (Android 1.6+):

@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);
}

Đây là một bản tóm tắt câu trả lời tuyệt vời của Arhimed.

Vui lòng đảm bảo bạn sử dụng API cấp 11 trở lên làm mục tiêu xây dựng dự án của bạn. Trong Eclipse, đó là Project > Properties > Android > Project Build Target. Điều này sẽ không phá vỡ khả năng tương thích ngược với các mức API thấp hơn. Đừng lo lắng, bạn sẽ gặp lỗi Lint nếu bạn vô tình sử dụng các tính năng được giới thiệu muộn hơn minSdkVersion. Nếu bạn thực sự muốn sử dụng tính năng giới thiệu muộn hơn minSdkVersion, bạn có thể ngăn chặn những lỗi sử dụng các chú thích, nhưng trong trường hợp đó, bạn cần chăm sóc về tính tương thích cho mình . Đây chính xác là những gì đã xảy ra trong đoạn mã trên.


3
Phải thay đổi TimerTask.THREAD_POOL_EXECUTOR thành AsynTask.THREAD_POOL_EXECUTOR thì ví dụ này hoạt động
Ronnie

2
Để vô hiệu hóa các vấn đề, bạn có thể thực hiện cuộc gọi chung chung: void startMyTask(AsyncTask<Void, ?, ?> asyncTask) (nếu doInBackground của bạn mất Void)
Peter

Bạn có thể đơn giản hóa mã trên hơn nữa nếu lớp của bạn mở rộng AsyncTask <Void, Integer, Void>. Nó làm việc rất tốt, cảm ơn.
Peter Arandorenko

1
Cảm ơn rất nhiều. Tôi đã có một kịch bản cần thực hiện đồng thời hai tác vụ async để đọc dữ liệu từ máy chủ web, nhưng tôi thấy máy chủ chỉ nhận được một URL và miễn là yêu cầu cụ thể đó không được hoàn thành, url của AsyncTask thứ hai sẽ không đánh vào máy chủ . Câu trả lời của bạn đã giải quyết vấn đề của tôi :)
Santhosh Gutta

1
Tôi tiếp tục nhận được upvote cho điều này, cảm ơn bạn. :) Nhưng tôi có ấn tượng nhiều người cố gắng sử dụng AsyncTask để kết nối mạng trong đó các thư viện tuyệt vời như Volley , Glide hoặc Swagger là lựa chọn tốt hơn. Hãy chắc chắn kiểm tra những điều này trước khi sử dụng AsyncTask :)
sulai

21

Làm cho đề xuất @sulai chung chung hơn:

@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);
}   

giải pháp tổng quát hơn: AsyncTaskCompat.executeParallel (MyAsyncTask mới, myprams);
Vikash Kumar Verma

11

Chỉ cần đưa bản cập nhật mới nhất (CẬP NHẬT 4) vào câu trả lời vô nhiễm của @Arhimed trong bản tóm tắt rất hay của @sulai:

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);
    }
}

Ồ wow, tuyệt vời. Điều này có nghĩa .executeOnExecutor()là hiện đang dư thừa cho API> = KITKAT, yeah?
TASlim Oseni


3

Đó là điều khả thi. Phiên bản thiết bị Android của tôi là 4.0.4 và android.os.Build.VERSION.SDK_INT là 15

Tôi có 3 spinners

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

Và tôi cũng có một lớp Async-Tack.

Đây là mã tải spinner của tôi

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();

Điều này đang làm việc hoàn hảo. Từng cái một spinners của tôi được tải. Tôi đã không sử dụng execOnExecutor.

Đây là lớp nhiệm vụ Async của tôi

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
Làm thế nào để bạn thấy rằng họ không được thực hiện tuần tự?
xử

@behelit Có vấn đề gì khi nó hoạt động không? Vui lòng đọc câu trả lời được chấp nhận để biết thêm chi tiết stackoverflow.com/a/4072832/2345900
Sajitha Rathnayake

2
Xin lỗi tôi không hiểu. Nếu bạn không thể biết liệu các asyncs của mình là tuần tự hay thực sự không đồng bộ thì điều đó không thành vấn đề. Ngoài ra, câu trả lời được liên kết nói rằng hãy sử dụng phương thức execonexecutor mà bạn không thực sự sử dụng trong câu trả lời của mình. Xin lỗi nếu tôi bỏ lỡ một cái gì đó.
xử vào

1
"Từng cái một spinners của tôi được tải." Bạn tự nói rằng đây là một thực thi nối tiếp, không phải là thực thi song song. Mỗi một trong số AsyncT Nhiệm vụ của chúng tôi được thêm vào hàng đợi và được xử lý tuần tự. Điều này không trả lời câu hỏi của OP.
Joshua Pinter

Đây là một câu trả lời rất cũ. Nếu điều này không hiệu quả với bạn, vui lòng tham khảo câu trả lời được chấp nhận
Sajitha Rathnayake

3

nếu bạn muốn thực thi các tác vụ song song, bạn cần gọi phương thức executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")sau phiên bản Android 3.0; nhưng phương thức này không tồn tại trước Android 3.0 và sau 1.6 vì nó tự thực thi song song, vì vậy tôi khuyên bạn nên tùy chỉnh lớp AsyncTask của riêng bạn trong dự án của bạn, để tránh ngoại lệ trong phiên bản Android khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.