Android AsyncTask线程限制?

Min*_*kas 95 multithreading android android-asynctask

我正在开发一个应用程序,我需要在每次用户登录系统时更新一些信息,我也在手机中使用数据库.对于所有这些操作(更新,从数据库中检索数据等),我使用异步任务.到目前为止,我不明白为什么我不应该使用它们,但最近我经历过,如果我做一些操作,我的一些异步任务只是停止预执行而不跳转到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");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后创建这个线程.它将进入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");
    }
}
Run Code Online (Sandbox Code Playgroud)

ant*_*nyt 207

所有AsyncTasks都由内部共享(静态)ThreadPoolExecutorLinkedBlockingQueue控制.当你调用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个仍在队列中.

  • 请注意,从Android 3.0+开始,默认的并发AsyncTasks数量已减少到1.更多信息:http://developer.android.com/reference/android/os/AsyncTask.html#execute(Params ...) (37认同)
  • “这是一个很好的理由,为什么您不应该将AsyncTasks用于长时间运行的操作”对于这种情况,您的建议是什么?手动生成新线程还是创建自己的执行程序服务? (2认同)
  • 执行程序基本上是线程之上的抽象,减少了编写复杂代码来管理它们的需要.它将您的任务与它们的执行方式分离开来.如果你的代码只依赖于执行程序,那么很容易透明地改变使用的线程数等等.我真的不能想到自己创建线程的好理由,因为即使对于简单的任务,工作量也是如此.执行者是相同的,如果不是更少. (2认同)

Zso*_*any 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);
   }
});
Run Code Online (Sandbox Code Playgroud)

或类似的东西

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

它甚至可以做得更多.在GitHub上查看.


rnk*_*rnk 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;
Run Code Online (Sandbox Code Playgroud)

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

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params)
Run Code Online (Sandbox Code Playgroud)

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

警告:允许多个任务从线程池并行运行通常不是您想要的,因为它们的操作顺序没有定义.例如,如果这些任务用于修改任何共同的状态(例如由于单击按钮而编写文件),则无法保证修改的顺序.如果没有仔细的工作,在较少的情况下,较新版本的数据可能会被较旧版本覆盖,从而导致数据丢失和稳定性问题变得模糊.这些变化最好连续执行; 为了保证这些工作是序列化的,无论平台版本如何,您都可以将此功能与SERIAL_EXECUTOR一起使用.

还有一点需要注意的是,框架提供的执行程序THREAD_POOL_EXECUTOR及其串行版本SERIAL_EXECUTOR(AsyncTask的默认设置)都是静态的(类级别构造),因此跨应用程序进程的所有AsyncTask实例共享.