小编典典

同时运行多个 AsyncTask —— 不可能吗?

all

我正在尝试同时运行两个 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 没有这个问题。嗯…


阅读 224

收藏
2022-05-12

共1个答案

小编典典

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

事实证明,对于使用“允许多个任务并行运行的线程池”的 API(从 1.6 开始到 3.0 结束),同时运行的 AsyncTask
的数量取决于已经传递了多少任务执行,但是他们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

从 3.0 开始,API 允许通过AsyncTask.executeOnExecutor(Executor exec, Params... params)方法使用您的自定义线程池执行器。例如,如果默认 10 不是您需要的,这允许配置延迟任务队列的大小。

正如@Knossos 所提到的,有一个选项可以使用AsyncTaskCompat.executeParallel(task, params);支持
v.4 库来并行运行任务,而无需担心 API 级别。此方法在 API 级别 26.0.0 中已弃用。

更新: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。应用程序仍然可以提交相同数量的任务。

2022-05-12