深入理解 Android:线程、同步、消息

Linux 中的 epoll 机制:

  • epoll 是 Linux 中高效的 IO 复用机制,显著提高大量并发连接下只有少数活跃时 CPU 的利用率;

  • epoll 无需要遍历整个被监听的文件描述符集合,只需遍历被内核 IO 事件异步唤醒而加入 Ready 队列的文件描述符集合;

  • epoll 不仅提供 select/poll 那种 IO 等待的水平触发(Level Triggered),还提供边缘触发(Edge Triggered),使用户程序能缓存 IO 状态,减少 epoll_wait/epoll_pwait 调用,提高效率;

  • epoll 用于保存事件的数据结构采用了红黑树,查找效率高;而 select/poll 采用数组保存,不仅一次能等待的句柄个数有限,而且查找速度慢;

线程(Thread.cpp):

  • 创建前判断 mCanCallJava 变量,如果为 true, 则先 attach() 到 JNI 环境,以便可以调用 JNI 函数;

  • 线程函数退出后,会从 JNI 环境 detach(),释放资源; 如果有未 detach() 的线程,直接 abort();

  • 无论 mCanCallJava 值为 truefalse,都会调用 _threadLoop() 函数,它运行在一个循环中,其返回值决定是否退出线程;

同步类(Thread.h):

Mutex:

  1. 构造函数传入 type,当 type==SHARED 表明支持跨进程的线程同步;

  2. 调用 lock()tryLock() 尝试加锁,根据返回值判断是否成功;系统保证每次只有一个线程加锁成功;

  3. 操作完后,调用 unlock() 释放锁;

AutoLock:

  1. 构造函数传入 Mutex 对象;

  2. 在析构函数中自动调用 unlock(),防止忘记调用造成死锁;

Condition:

  1. 构造函数传入 type,当 type==SHARED 表明支持跨进程的条件同步;

  2. wait(&mutex)等待;

  3. waitRelative(&mutex,time),在 time 时间内等待,超时退出等待;

  4. signal() 通知当前等待的线程;

  5. broadcast() 通知所有等待的线程;

  6. Condition 对象的方法必须放在 Mutex 对象的 lock()unlock() 之间的范围调用;

原子操作(Atomic.c):

  • 原子操作即最小执行单位,在执行前不会被其他操作打断;

  • 主要函数:

    • android_atomic_write(): 原子写操作;

    • android_atomic_inc(): 原子自增操作;

    • android_atomic_dec(): 原子自减操作;

    • android_atomic_add(): 原子加法操作;

    • android_atomic_and(): 原子与操作;

    • android_atomic_or(): 原子或操作;

    • android_atomic_cmpxchg(): 原子条件交换操作;

Looper:

  • 功能:封装消息队列,循环读取和分发消息;

  • 主要方法:

    • Looper(): 创建 MessageQueue,关联当前线程;

    • prepare(): 判断关联的 Thread 是否存在 Looper 对象(主线程默认会自动创建),不存在则 new 一个 Looper;

    • loop(): while(true) 循环,调用 queue.next() 读取 message,调用 messagetarget (即 Handler) 的 dispatchMessage() 方法分发消息;

Handler:

  1. 发送消息,实际上就是填充消息到关联的 Looper 的消息队列;

  2. Messagetarget 设置为自己,因为消息的处理工作也在 Handler 中;

HandlerThread:

通过 synchronized 解决了 Looper 可能为空的情况;

MessageQueue:

  • Android 2.3 以后,核心部分已放到 Native 层的 NativeMessageQueue;它先处理 Native 的 Message,再处理 Native 的 Request,最后处理 Java 层的 Message;

  • enqueueMessage() 方法调用了 Native 层 Looperwake(),向管道写入 "W" 以触发结束等待;

  • next() 方法调用了 Native 层 LooperpollOnce(timeoutMillis, outFd, outEvents, outData)

    • 参数 timeoutMillis 为超时等待时间,-1 无需等待,0 立即返回;

    • 参数 outFd 存储发生事件的文件描述符;

    • 参数 outEvents 存储该文件描述符上发生的事件(可读、可写、错误、中断),这些事件是从 epoll 事件转化来的;

    • 参数 outData 存储上下文数据,该数据由用户在添加监听时传递;

    • epoll_wait() 检测到某些文件句柄有事件而返回,如果是管道读端发生事件,则认为是控制命令,直接读取数据;如果是其他 Fd 发生事件,则根据 Request 构造 Response,并 push 到 Response 数组;

    • 首先调用 Native 的 Handler 处理 Native 的 Message,然后处理 Response 数组中带有 callback 的事件;

    • 调用 Native 层 Looper.addFd() 可添加监控请求,实际调用 epoll_ctl 增加文件描述符;


参考:

《深入理解 Android: 卷 I》