我正在尝试同时运行两个 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."); } }
我期望的输出是:
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 任务会产生输出。
我是否在这里遗漏了一些明显的东西和/或做了一些愚蠢的事情?不能同时运行两个 AsyncTasks 吗?
编辑:我意识到有问题的手机运行 Android 1.5,我更新了问题描述。因此。运行 Android 2.1 的 HTC Hero 没有这个问题。嗯…
AsyncTask 使用线程池模式来运行来自 doInBackground() 的内容。问题最初是(在早期的 Android 操作系统版本中)池大小仅为 1,这意味着一堆 AsyncTask 没有并行计算。但后来他们修复了这个问题,现在大小为 5,所以最多 5 个 AsyncTask 可以同时运行。不幸的是,我不记得他们到底在哪个版本中改变了这一点。
更新:
以下是当前 (2012-01-27) API 对此的说明:
首次引入时,AsyncTask 在单个后台线程上串行执行。从 DONUT 开始,这被更改为允许多个任务并行运行的线程池。在 HONEYCOMB 之后,计划将其改回单线程,以避免并行执行导致的常见应用程序错误。如果你真的想要并行执行,你可以使用这个方法的 executeOnExecutor(Executor, Params…) 版本和 THREAD_POOL_EXECUTOR; 但是,请参阅那里的评论以获取有关其使用的警告。
DONUT 是 Android 1.6,HONEYCOMB 是 Android 3.0。
更新:2
查看kabuko来自的评论Mar 7 2012 at 1:27。
kabuko
Mar 7 2012 at 1:27
事实证明,对于使用“允许多个任务并行运行的线程池”的 API(从 1.6 开始到 3.0 结束),同时运行的 AsyncTask 的数量取决于已经传递了多少任务执行,但是他们doInBackground()还没有完成。
doInBackground()
这是我在 2.2 上测试/确认的。假设您有一个自定义 AsyncTask,它只在doInBackground(). AsyncTask 在内部使用固定大小的队列来存储延迟任务。队列大小默认为 10。如果您连续启动 15 个自定义任务,那么前 5 个将进入它们的doInBackground(),但其余的将在队列中等待空闲的工作线程。只要前 5 个任务中的任何一个完成,从而释放一个工作线程,队列中的一个任务就会开始执行。所以在这种情况下,最多 5 个任务将同时运行。但是,如果您连续启动 16 个自定义任务,那么前 5 个将进入它们的doInBackground(),其余 10 个将进入队列,但是对于第 16 个,将创建一个新的工作线程,因此它将立即开始执行。所以在这种情况下,最多 6 个任务将同时运行。
可以同时运行多少个任务是有限制的。由于AsyncTask使用的线程池执行器具有有限的最大工作线程数 (128),并且延迟任务队列的大小固定为 10,因此如果您尝试执行超过 138 个自定义任务,应用程序将崩溃java.util.concurrent.RejectedExecutionException。
AsyncTask
java.util.concurrent.RejectedExecutionException
从 3.0 开始,API 允许通过AsyncTask.executeOnExecutor(Executor exec, Params... params)方法使用您的自定义线程池执行器。例如,如果默认 10 不是您需要的,这允许配置延迟任务队列的大小。
AsyncTask.executeOnExecutor(Executor exec, Params... params)
正如@Knossos 所提到的,有一个选项可以使用AsyncTaskCompat.executeParallel(task, params);支持 v.4 库来并行运行任务,而无需担心 API 级别。此方法在 API 级别 26.0.0 中已弃用。
AsyncTaskCompat.executeParallel(task, params);
更新: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。应用程序仍然可以提交相同数量的任务。