【问题标题】:HandlerThread vs Executor - When is one more appropriate over the other?HandlerThread vs Executor - 什么时候比另一个更合适?
【发布时间】:2011-11-19 16:45:08
【问题描述】:

我只是好奇在某些时候我应该选择Executor 而不是HandlerThread。有时一个比另一个优越,还是我真的应该坚持使用HandlerThread?就我而言,我目前正在收听ServerSocket 的连接,并在Executor 创建的单独线程上处理每个请求。尽管我举了一个具体的例子,但我真的只是在寻找一种比另一种更合适的情况。不过,我欢迎 cmets 关于我的设计。

【问题讨论】:

    标签: android concurrency


    【解决方案1】:

    Executor 类更强大,可以使用线程池,而每个 Handler 引用单个线程。 Executor 允许您获取所有计划任务并根据需要取消它们。另一方面,处理程序不会回答简单的问题,例如有多少任务正在等待或给我所有等待任务的参考。我认为 Handler 受到更多限制的一个原因是,Android 允许您访问它用于 UI 的主 Handler,如果您开始取消操作系统任务,您可能真的会搞砸操作系统。

    一般来说,如果您需要一个线程池或大量功能,请使用 Executor。如果您只需要一个好的后台线程来一次运行一个任务,请使用处理程序。例如,当我想查询我的数据库时,我真的只希望一次发生一个查询并且我不想生成 ANR,因此我使用在后台线程上运行的 Handler 来运行我的查询。

    我相信您选择的 executor 听起来很合适,因为您想同时处理多个传入请求,而 Handler 一次只能处理一个。

    更新:如何创建在后台线程上运行的处理程序:

    在您的构造函数或 onCreate 中编写以下内容,显然您可以将优先级设置为您喜欢的任何内容:

    public class MyClass {
    
        private Handler mBgHandler;
    
        public MyClass() {
            HandlerThread bgThread = new HandlerThread("My-Background-Handler");
            bgThread.start();
            mBgHandler = new Handler(bgThread.getLooper());
        }
    }
    

    更新:完成后不要忘记 quit() 或 quitSafely() HandlerThread,否则它将永远等待

    【讨论】:

    • HandlerThread 用于获取在后台线程上运行的 Handler。用户工程师会与 HandlerThread 进行很多交互。我已经更新了我的答案,以展示您如何使用 HandlerThread 创建处理程序。
    • @satur9nine 关于您的以下评论:我相信处理程序受到更多限制的原因是,Android 允许您访问主线程,如果您开始取消操作系统,您可能真的会搞砸操作系统任务。 你试过 Handler.getLooper().getThread() 吗?它应该使您可以访问底层线程。
    • 我的意思是,Android 允许您访问用于在主线程上安排基本内部任务的处理程序。如果您要取消计划或以其他方式修改这些会扰乱 Android 操作系统的内部任务。
    • 我也发现它非常有限,因为我无法从 looper 中获取消息队列。我的等待时间很长,我想看看是什么问题,但没有运气。
    【解决方案2】:

    截至 2011 年 12 月 22 日,我不会遵循 satur9nine 回答中的示例代码。

    Thread.MIN_PRIOROTY 映射到 android.os.Process.THREAD_PRIORITY_LOWEST。 Quote:

    最低可用线程优先级。仅适用于那些真的,真的不想在发生其他事情时跑步的人。

    我至少会使用 android.os.Process.THREAD_PRIORITY_BACKGROUND,就像这样:

    HandlerThread bgThread = new HandlerThread("handler name");
    Process.setThreadPriority(bgThread.getThreadId(), Process.THREAD_PRIORITY_BACKGROUND);
    bgThread.start();
    mBgHandler = new Handler(bgThread.getLooper());
    

    这会将默认的 Android 背景优先级分配给线程。

    目前,优先级 Process.THREAD_PRIORITY_BACKGROUND 和更低优先级的线程通过 Linux cgroup 共享人为限制的 CPU 时间,例如 here。如果后台任务不只是等待 I/O 而是执行实际计算,我会考虑通过 android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE 提高其优先级,这(当前)将其移出后台 cgroup,同时仍然不会严重危及 UI 和实时活动。

    更新: satur9nine 的答案已在 2013 年 1 月 8 日悄悄修改,不再设置最低优先级。 HandlerThread 现在将隐式具有 android.os.Process.THREAD_PRIORITY_BACKGROUND 的优先级。这意味着它现在获得了默认的后台任务优先级,但它仍然被限制为消耗最多 10% 的 CPU 时间以及可能存在的所有其他后台任务。如果不需要,请使用我上面的代码,例如与

    Process.setThreadPriority(bgThread.getThreadId(),
                              Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
    

    将后台线程从 cgroup 中取出。

    【讨论】:

    • 使用该命令序列,getThreadId 返回 -1 并中断Process.setThreadPriority()。我错过了一些非常明显的东西,今天可能最好退出编码
    • @superjos 查看相关Android版本的API文档,查看Process.setThreadPriority()是否不允许,或者new HandlerThread()是否失败。然后,消除问题。
    • 感谢您的回复@class-stacker。完全没看懂,能和我讲道理吗? new HandlerThread() 电话对我来说不会失败。但是bgThread.getThreadId() 返回 -1。当该值传递给Process.setThreadPriority() 时,它会导致它抛出。我检查了文档(和帖子),有很多使用setThreadPriority 的例子。你知道更多吗?
    • @superjos 发布一个包含您的代码相关部分的问题,然后给我留言并附上它的链接?
    猜你喜欢
    • 2011-03-20
    • 2010-11-05
    • 2018-05-09
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 2017-12-15
    相关资源
    最近更新 更多