1. ホーム
  2. アンドロイド

[解決済み】複数のAsyncTasksを同時に実行することは不可能か?

2022-04-16 09:16:05

質問

2つのAsyncTasksを同時に実行しようとしています。(プラットフォームはAndroid 1.5、HTC Heroです。) しかし、最初の1つだけが実行されます。以下は、私の問題を説明するための簡単なスニペットです。

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

2番目のAsyncTaskは決して実行されません。execute()文の順番を変えると、fooタスクだけが出力を生成します。

私は何か明らかなことを見逃しているのでしょうか、それとも何か愚かなことをしているのでしょうか?2つのAsyncTasksを同時に実行することは不可能なのでしょうか?

編集:問題の携帯電話がAndroid 1.5であることに気づいたので、それに応じて問題の記述を更新しました。Android 2.1を搭載したHTC Heroでは、この問題は発生しません。うーん.

解決方法は?

AsyncTask は doInBackground() からの処理を実行するためにスレッドプールパターンを使用します。問題は、当初(初期の Android OS バージョン)、プールのサイズが 1 だけだったことで、多数の AsyncTasks に対して並列計算が行われないことを意味します。しかし、その後修正され、現在は5となり、最大で5つのAsyncTasksが同時に実行できるようになった。残念ながら、どのバージョンで変更されたかは覚えていません。

UPDATEです。

現在(2012-01-27)のAPIによると、以下の通りです。

<ブロッククオート

最初に導入されたとき、AsyncTasksは1つのバックグラウンドスレッドで連続的に実行されました。DONUT以降、これはスレッドのプールに変更され、複数のタスクが並行して実行されるようになりました。HONEYCOMB以降では、並列実行による一般的なアプリケーションのエラーを避けるために、これをシングルスレッドに戻すことが計画されています。本当に並列実行が必要な場合は、このメソッドのexecuteOnExecutor(Executor, Params...)バージョンをTHREAD_POOL_EXECUTORで使用できますが、その使用に関する警告は、そこの解説を参照してください。

DONUTはAndroid 1.6、HONEYCOMBはAndroid 3.0です。

UPDATE: 2

のコメントをご覧ください。 kabuko から Mar 7 2012 at 1:27 .

1.6から3.0までのAPIでは、同時に実行されるAsyncTasksの数は、実行のために渡されたものの、まだ終了していないタスクの数に依存することが判明しています。 doInBackground() を使用します。

これは2.2で私がテスト/確認したものです。で1秒だけスリープするカスタムAsyncTaskがあるとします。 doInBackground() . AsyncTasksは、遅延タスクを格納するために、内部で固定サイズのキューを使用します。キュー・サイズはデフォルトで10です。もし、15個のカスタムタスクを連続して開始した場合、最初の5個はそれぞれの doInBackground() しかし、残りは空いているワーカスレッドを待つためにキューで待機します。最初の5つのうちどれかが終了してワーカスレッドが解放されると、キューからタスクが実行を開始します。したがって、この場合、最大で5つのタスクが同時に実行されることになります。しかし、16個のカスタムタスクを連続して起動した場合、最初の5個は doInBackground() 残りの10個はキューに入りますが、16個目については新しいワーカスレッドが作成され、すぐに実行が開始されます。この場合、同時に実行されるタスクはせいぜい6個です。

同時に実行できるタスクの数には限界があります。というのも AsyncTask はワーカスレッドの最大数 (128) が制限されたスレッドプールエクゼキュータを使用し、遅延タスクキューは固定サイズ 10 であるため、138 以上のカスタムタスクを実行しようとすると 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で非推奨となりました。

UPDATE: 3

タスクの数、シリアル実行とパラレル実行を試す簡単なテストアプリを紹介します。 https://github.com/vitkhudenko/test_asynctask

UPDATE: 4 (@penkzhou さんのご指摘に感謝します)

Android 4.4から AsyncTask で説明したものとは異なる挙動をします。 アップデイト:2 セクションをご覧ください。そこで が修正されました。 を防ぐために 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 に減少します。アプリはこれまでと同じ数のタスクを送信できます。