rod*_*ion 249 multithreading android android-asynctask
我正在尝试同时运行两个AsyncTasks.(平台是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.");
}
}
Run Code Online (Sandbox Code Playgroud)
我期望的输出是:
onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo
Run Code Online (Sandbox Code Playgroud)
等等.但是,我得到的是:
onCreate() is done.
bar bar bar
bar bar bar
bar bar bar
Run Code Online (Sandbox Code Playgroud)
第二个AsyncTask永远不会被执行.如果我改变execute()语句的顺序,只有foo任务会产生输出.
我错过了一些明显的东西和/或做一些愚蠢的事情吗?是不是可以同时运行两个AsyncTasks?
编辑:我意识到有问题的手机运行Android 1.5,我更新了问题descr.因此.运行Android 2.1的HTC Hero没有这个问题.嗯......
Vit*_*nko 428
AsyncTask使用线程池模式来运行doInBackground()中的东西.问题最初(在早期的Android OS版本中)池大小只有1,这意味着没有对一堆AsyncTasks进行并行计算.但后来他们修复了这个问题,现在大小为5,所以最多可以同时运行5个AsyncTasks.不幸的是我不记得他们改变了什么版本.
更新:
以下是当前(2012-01-27)API对此的说法:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. After HONEYCOMB, it is planned to change this back to a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.
DONUT is Android 1.6, HONEYCOMB is Android 3.0.
UPDATE: 2
See the comment by kabuko from Mar 7 2012 at 1:27.
It turns out that for APIs where "a pool of threads allowing multiple tasks to operate in parallel" is used (starting from 1.6 and ending on 3.0) the number of simultaneously running AsyncTasks depends on how many tasks have been passed for execution already, but have not finished their doInBackground() yet.
This is tested/confirmed by me on 2.2. Suppose you have a custom AsyncTask that just sleeps a second in doInBackground(). AsyncTasks use a fixed size queue internally for storing delayed tasks. Queue size is 10 by default. If you start 15 your custom tasks in a row, then first 5 will enter their doInBackground(), but the rest will wait in a queue for a free worker thread. As soon as any of the first 5 finishes, and thus releases a worker thread, a task from the queue will start execution. So in this case at most 5 tasks will run simultaneously. However if you start 16 your custom tasks in a row, then first 5 will enter their doInBackground(), the rest 10 will get into the queue, but for the 16th a new worker thread will be created so it'll start execution immediately. So in this case at most 6 tasks will run simultaneously.
There is a limit of how many tasks can be run simultaneously. Since AsyncTask uses a thread pool executor with limited max number of worker threads (128) and the delayed tasks queue has fixed size 10, if you try to execute more than 138 your custom tasks the app will crash with java.util.concurrent.RejectedExecutionException.
Starting from 3.0 the API allows to use your custom thread pool executor via AsyncTask.executeOnExecutor(Executor exec, Params... params) method. This allows, for instance, to configure the size of the delayed tasks queue if default 10 is not what you need.
As @Knossos mentions, there is an option to use AsyncTaskCompat.executeParallel(task, params); from support v.4 library to run tasks in parallel without bothering with API level. This method became deprecated in API level 26.0.0.
UPDATE: 3
Here is a simple test app to play with number of tasks, serial vs. parallel execution: https://github.com/vitkhudenko/test_asynctask
UPDATE: 4 (thanks @penkzhou for pointing this out)
Starting from Android 4.4 AsyncTask behaves differently from what was described in UPDATE: 2 section. There is a fix to prevent AsyncTask from creating too many threads.
Before Android 4.4 (API 19) AsyncTask had the following fields:
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);
Run Code Online (Sandbox Code Playgroud)
In Android 4.4 (API 19) the above fields are changed to this:
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);
Run Code Online (Sandbox Code Playgroud)
This change increases the size of the queue to 128 items and reduces the maximum number of threads to the number of CPU cores*2 + 1. Apps can still submit the same number of tasks.
sul*_*lai 211
这允许在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);
}
Run Code Online (Sandbox Code Playgroud)
这是Arhimed的优秀答案的摘要.
请确保使用API级别11或更高级别作为项目构建目标.在Eclipse中,就是这样Project > Properties > Android > Project Build Target.这不会破坏向下API级别的向后兼容性.不用担心,如果您不小心使用了以后引入的功能,您将收到Lint错误minSdkVersion.如果你真的想不迟于使用中引入的功能minSdkVersion,所以可以使用标注抑制这些错误,但在这种情况下,你需要照顾兼容性自己.这正是上面代码片段中发生的事情.
Asa*_*afK 20
使@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);
}
Run Code Online (Sandbox Code Playgroud)
只是在@sulai的非常好的总结中包含最新更新(更新4)@Arhimed完美无瑕的答案:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
166724 次 |
| 最近记录: |