[解決済み】複数のAsyncTasksを同時に実行することは不可能か?
質問
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 に減少します。アプリはこれまでと同じ数のタスクを送信できます。
関連
-
[解決済み】パッケージ名(Google Analytics)に一致するクライアントが見つからない - 複数のproductFlavorsとbuildTypes
-
[解決済み】react-native: コマンドが見つかりません。
-
[解決済み】apkインストール時のINSTALL_FAILED_NO_MATCHING_ABIS
-
[解決済み】リストビューにonclicklistenerを追加する(android)
-
[解決済み】apkファイルのインストール中に「パッケージが破損しているようです」というエラーが発生する。
-
[解決済み】新しいAVDを作成すると、CPU/ABIフィールドに「システムイメージがインストールされていません」と表示される。
-
[解決済み] コンパイルした.apkを端末にインストールしようとするとINSTALL_FAILED_UPDATE_INCOMPATIBLEが表示される
-
[解決済み] Looper.prepare()を呼び出していないスレッドではハンドラを作成できない
-
[解決済み] BIOSのセキュリティ設定でVT-xを有効にする(お使いのコンピュータのドキュメントを参照)。
-
[解決済み] Android SDK の場所には空白を含めないでください。NDK ツールで問題が発生するためです。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Android - SDKバージョン23のアップデート後、ACTION-VIEWインテントフィルタを持つアクティビティを少なくとも1つ追加する。
-
[解決済み】まだ警告が出る:設定 'compile' は時代遅れで 'implementation' に置き換わった。
-
[解決済み】シンボル 'AppCompatActivity' を解決できない。
-
[解決済み] SDKの場所がandroid studioで見つからない
-
[解決済み] エラー - Android リソースのリンクに失敗しました (AAPT2 27.0.3 Daemon #0)
-
[解決済み] Androidのgravityとlayout_gravityの違いは何ですか?
-
[解決済み] Android Fragment no view found for ID?
-
[解決済み] Android: @drawable/picture を drawable に変換するのに失敗しました。
-
[解決済み] Android Fragment onAttach() 非推奨
-
[解決済み] ハンドラ vs AsyncTask vs スレッド [終了しました]。