Android AsyncTask 线程限制?
Posted
技术标签:
【中文标题】Android AsyncTask 线程限制?【英文标题】:Android AsyncTask threads limits? 【发布时间】:2012-03-28 02:09:23 【问题描述】:我正在开发一个应用程序,每次用户登录系统时我都需要更新一些信息,我还使用手机中的数据库。对于所有这些操作(更新、从数据库检索数据等),我使用异步任务。到目前为止,我不明白为什么不应该使用它们,但最近我体验到,如果我执行一些操作,我的一些异步任务只会在预执行时停止并且不会跳转到 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");
【问题讨论】:
【参考方案1】:所有 AsyncTask 都由共享(静态)ThreadPoolExecutor 和 LinkedBlockingQueue 在内部控制。当您在 AsyncTask 上调用 execute
时,ThreadPoolExecutor
将在未来某个时间准备好时执行它。
“我什么时候准备好?” ThreadPoolExecutor
的行为由两个参数控制,核心池大小和最大池大小。如果当前活动的线程少于核心池大小并且有新作业进入,则执行程序将创建一个新线程并立即执行它。如果至少有核心池大小的线程在运行,它将尝试将作业排队并等待直到有空闲线程可用(即直到另一个作业完成)。如果无法将作业排队(队列可以有最大容量),它将创建一个新线程(最多为最大池大小线程)供作业运行。非核心空闲线程最终可以退役根据保活超时参数。
在 Android 1.6 之前,核心池大小为 1,最大池大小为 10。从 Android 1.6 开始,核心池大小为 5,最大池大小为 128。队列大小在这两种情况下都是 10 . keep-alive 超时时间是 2.3 之前的 10 秒,之后的 1 秒。
考虑到所有这些,现在很清楚为什么 AsyncTask
似乎只会执行 5/6 的任务。第 6 个任务正在排队等待其他任务之一完成。这是您不应该将 AsyncTasks 用于长时间运行的操作的一个很好的理由 - 它会阻止其他 AsyncTasks 运行。
为了完整起见,如果您重复练习超过 6 个任务(例如 30 个),您将看到超过 6 个将进入doInBackground
,因为队列将变满并且执行程序被推送以创建更多工作线程。如果您继续执行长时间运行的任务,您应该会看到 20/30 变为活动状态,其中 10 仍在队列中。
【讨论】:
“这是一个很好的理由,为什么您不应该将 AsyncTasks 用于长时间运行的操作”您对这种情况有什么建议?手动生成新线程还是创建自己的执行器服务? 执行器基本上是线程之上的抽象,减轻了编写复杂代码来管理它们的需要。它将您的任务与它们应该如何执行分离。如果您的代码仅依赖于执行程序,那么很容易透明地更改使用了多少线程等。我真的想不出自己创建线程的充分理由,因为即使对于简单的任务,使用的工作量一个 Executor 是相同的,如果不是更少的话。 请注意,从 Android 3.0+ 开始,默认的并发 AsyncTask 数量已减少到 1。更多信息:developer.android.com/reference/android/os/… 哇,非常感谢您的出色回答。最后,我解释了为什么我的代码会如此零星和神秘地失败。 @antonyt,还有一个疑问,已取消的 AsyncTasks,它会计入 AsyncTasks 的数量吗?即,计入core pool size
或maximum pool size
?【参考方案2】:
@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 年前 :(【参考方案3】:更新:自 API 19 起,核心线程池大小已更改以反映设备上的 CPU 数量,开始时最少 2 个,最多 4 个,同时增长到最大值CPU*2 +1 - Reference
// 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 的默认执行器是串行的(一次执行一个任务,并按照它们到达的顺序),但 method
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params)
您可以提供一个 Executor 来运行您的任务。您可以提供 THREAD_POOL_EXECUTOR 幕后执行程序,但没有任务序列化,或者您甚至可以创建自己的执行程序并在此处提供。 但是,请仔细注意 Javadocs 中的警告。
警告:允许多个任务从线程池并行运行通常不是人们想要的,因为它们的操作顺序没有定义。例如,如果这些任务用于修改任何共同的状态(例如由于单击按钮而写入文件),则无法保证修改的顺序。如果不仔细工作,在极少数情况下,较旧版本的数据可能会覆盖较新版本的数据,从而导致难以理解的数据丢失和稳定性问题。此类更改最好按顺序执行;为了保证无论平台版本如何都可以序列化此类工作,您可以将此函数与 SERIAL_EXECUTOR 一起使用。
还有一点需要注意的是,框架提供的执行器 THREAD_POOL_EXECUTOR 及其串行版本 SERIAL_EXECUTOR(这是 AsyncTask 的默认设置)都是静态的(类级构造),因此在您的应用程序进程中的所有 AsyncTask 实例之间共享.
【讨论】:
以上是关于Android AsyncTask 线程限制?的主要内容,如果未能解决你的问题,请参考以下文章
Android 对线程封装了:AsyncTask, HandlerThread和线程池。 有知道这三个如何选择吗?