深入理解 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
值为true
或false
,都会调用_threadLoop()
函数,它运行在一个循环中,其返回值决定是否退出线程;
同步类(Thread.h):
Mutex:
构造函数传入
type
,当type==SHARED
表明支持跨进程的线程同步;调用
lock()
或tryLock()
尝试加锁,根据返回值判断是否成功;系统保证每次只有一个线程加锁成功;操作完后,调用
unlock()
释放锁;
AutoLock:
构造函数传入
Mutex
对象;在析构函数中自动调用
unlock()
,防止忘记调用造成死锁;
Condition:
构造函数传入
type
,当type==SHARED
表明支持跨进程的条件同步;wait(&mutex)
等待;waitRelative(&mutex,time)
,在time
时间内等待,超时退出等待;signal()
通知当前等待的线程;broadcast()
通知所有等待的线程;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
,调用message
的target
(即Handler
) 的dispatchMessage()
方法分发消息;
Handler:
发送消息,实际上就是填充消息到关联的
Looper
的消息队列;将
Message
的target
设置为自己,因为消息的处理工作也在Handler
中;
HandlerThread:
通过 synchronized
解决了 Looper
可能为空的情况;
MessageQueue:
Android 2.3 以后,核心部分已放到 Native 层的
NativeMessageQueue
;它先处理 Native 的Message
,再处理 Native 的Request
,最后处理 Java 层的Message
;enqueueMessage()
方法调用了 Native 层Looper
的wake()
,向管道写入"W"
以触发结束等待;next()
方法调用了 Native 层Looper
的pollOnce(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
增加文件描述符;
参考: