Android AsyncTask线程限制?


95

我正在开发一个应用程序,每次用户登录系统时我都需要更新一些信息,我也在电话中使用数据库。对于所有这些操作(更新,从db检索数据等),我使用异步任务。到现在为止,我还不明白为什么不使用它们,但是最近我体验到,如果我执行一些操作,一些异步任务只会在执行前停止,而不会跳转到doInBackground。真是太奇怪了,不能像现在这样保留它,所以我开发了另一个简单的应用程序来检查问题所在。奇怪的是,当异步任务总数达到5个时,我会得到相同的行为,第6个异步任务在预执行时停止。

android在Activity / App上是否有asyncTasks的限制?还是只是一些错误,应该报告?有没有人遇到过同样的问题,也许找到了解决方法?

这是代码:

只需创建其中的5个线程即可在后台工作:

private class LongAsync extends AsyncTask<String, Void, String>
{
    @Override
    protected void onPreExecute()
    {
        Log.d("TestBug","onPreExecute");
        isRunning = true;
    }

    @Override
    protected String doInBackground(String... params)
    {
        Log.d("TestBug","doInBackground");
        while (isRunning)
        {

        }
        return null;
    }

    @Override
    protected void onPostExecute(String result)
    {
        Log.d("TestBug","onPostExecute");
    }
}

然后创建这个线程。它将输入preExecute并挂起(不会转到doInBackground)。

private class TestBug extends AsyncTask<String, Void, String>
{
    @Override
    protected void onPreExecute()
    {
        Log.d("TestBug","onPreExecute");

        waiting = new ProgressDialog(TestActivity.this);
        waiting.setMessage("Loading data");
        waiting.setIndeterminate(true);
        waiting.setCancelable(true);
        waiting.show();
    }

    @Override
    protected String doInBackground(String... params)
    {
        Log.d("TestBug","doInBackground");
        return null;
    }

    @Override
    protected void onPostExecute(String result)
    {
        waiting.cancel();
        Log.d("TestBug","onPostExecute");
    }
}

Answers:


207

所有AsyncTasks都由一个共享的(静态)ThreadPoolExecutor和一个LinkedBlockingQueue内部控制。当您调用executeAsyncTask时,ThreadPoolExecutor它将在以后准备就绪时执行。

“我什么时候准备好了?” a的行为ThreadPoolExecutor由两个参数控制,即核心池大小最大池大小。如果当前活动的线程池少于核心池大小,并且有新的作业进入,则执行程序将创建一个新线程并立即执行它。如果至少有核心池大小的线程在运行,它将尝试将作业排队,并等待直到有空闲线程可用(即,直到另一个作业完成)为止。如果无法将作业排队(队列可以具有最大容量),它将为要运行的作业创建一个新线程(最大池大小的线程)。非核心空闲线程最终可以退役根据保持活动超时参数。

在Android 1.6之前,核心池大小为1,最大池大小为10。从Android 1.6开始,核心池大小为5,最大池大小为128。在两种情况下,队列的大小均为10。保持活动超时是2.3之前的10秒,此后是1秒。

考虑到所有这些因素后,现在可以清楚地知道为什么AsyncTask将仅执行5/6的任务。第6个任务正在排队,直到其他任务之一完成。这是一个很好的理由,为什么您不应该将AsyncTasks用于长时间运行的操作-这将阻止其他AsyncTasks运行。

为了完整起见,如果您重复执行超过6个任务(例如30个)的练习,您将看到输入6个以上的任务,doInBackground因为队列将变满,执行程序被推入以创建更多工作线程。如果继续执行长期运行的任务,则应该看到20/30处于活动状态,而10/30仍在队列中。


2
“这是一个很好的理由,为什么您不应该将AsyncTasks用于长时间运行的操作”对于这种情况,您的建议是什么?手动生成新线程还是创建自己的执行器服务?
user123321

2
执行程序基本上是线程之上的抽象,从而减轻了编写复杂代码来管理线程的需要。它使您的任务与应如何执行脱钩。如果您的代码仅取决于执行程序,那么很容易透明地更改使用的线程数,依此类推。我真的没有想到自己创建线程的充分理由,因为即使对于简单的任务,其工作量也很大。执行器是相同的,即使不是更少。
antonyt 2012年

37
请注意,从Android 3.0以上版本,并发AsyncTasks的默认数量已减少到1更多信息:developer.android.com/reference/android/os/...
基兰

哇,非常感谢您的出色回答。最后,我对为什么我的代码如此零星而神秘地失败了一个解释。
克里斯·奈特

@antonyt,还有一个疑问,AsyncTasks被取消了,它将算作AsyncTasks的数量吗?即算在core pool sizemaximum pool size
nmxprime 2014年

9

@antonyt的答案是正确的,但是如果您正在寻找简单的解决方案,则可以签出Needle。

借助它,您可以定义自定义线程池的大小,并且与不同AsyncTask,它可以在所有 Android版本上相同。有了它,您可以说出类似以下内容:

Needle.onBackgroundThread().withThreadPoolSize(3).execute(new UiRelatedTask<Integer>() {
   @Override
   protected Integer doWork() {
       int result = 1+2;
       return result;
   }

   @Override
   protected void thenDoUiRelatedWork(Integer result) {
       mSomeTextView.setText("result: " + result);
   }
});

或类似的东西

Needle.onMainThread().execute(new Runnable() {
   @Override
   public void run() {
       // e.g. change one of the views
   }
}); 

它可以做的更多。在GitHub签出


最后一次提交是在5年前:(
LukaszTaraszka '19

5

更新:从API 19开始,更改了核心线程池大小以反映设备上的CPU数量,启动时最少2个,最多4个,同时增加到最大CPU * 2 +1- 参考

// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

还要注意,虽然AsyncTask的默认执行程序是串行的(每次执行一个任务,并以它们到达的顺序执行),但使用方法

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params)

您可以提供执行程序来运行您的任务。您可以提供高级执行程序THREAD_POOL_EXECUTOR,但不执行任务序列化,或者甚至可以创建自己的执行程序并在此处提供。但是,请仔细注意Javadocs中的警告。

警告:通常并不需要从线程池中并行运行多个任务,因为未定义其操作顺序。例如,如果使用这些任务来共同修改任何状态(例如,由于单击按钮而写入文件),则不能保证修改的顺序。如果不进行仔细的工作,在极少数情况下,较新版本的数据可能会被较旧的版本覆盖,从而导致模糊的数据丢失和稳定性问题。最好以串行方式执行此类更改;为了确保无论平台版本如何都可以序列化这些工作,可以将此功能与SERIAL_EXECUTOR一起使用。

还有一点要注意的是,提供的执行程序THREAD_POOL_EXECUTOR框架及其串行版本SERIAL_EXECUTOR(AsyncTask的默认版本)都是静态的(类级别构造),因此在您的应用程序过程中的所有AsyncTask实例之间都是共享的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.