• 【从源代码看Android】02MessageQueue的epoll原型


    版权声明:本文为博主原创文章,欢迎转载。请注明原文链接 https://blog.csdn.net/ashqal/article/details/31772697

    1 开头

    上一讲讲到Looper,大家对Looper有了大概的了结(好几个月过去了…)

    大家都知道一个Handler相应有一个MessageQueue,

    在哪个线程上new Handler(假设不指定looper对象),那么这个handler就默认相应于这个线程上的prepare过的Looper

    例如以下图Handler.java代码所看到的,mLooper由Looper.myLooper()指定,

    public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }

    而Looper.myLooper()来自此线程里保存的looper对象(在looper.prepare时存入)

    public static Looper myLooper() {
            return sThreadLocal.get();
        }

    so。一个handler。相应了一套MessageQueue、Thread、Looper

    这些都是 【从源代码看Android】01从Looper说起 讲过的东西,那么以下来些硬货



    2 一个问题引入

    从一个问题引入,假设在子线程12上创建了一个handler,

    如今在主线程上调用handler.sendEmptyMessage,

    handler怎样在主线程上处理这个msg,

    然后从子线程12让handler的handleMessage函数处理呢?


    那么这个时候就要引入一个跨线程的事件模型--epoll,

    这一讲先把cpp epoll模型讲清楚,

    下一讲再讲android里怎样利用这个模型的



    3 epoll模型

    epolldemo.cpp

    #include <iostream>
    #include <vector>
    #include <queue>
    #include <pthread.h>
    #include <unistd.h>
    #include <sys/epoll.h>
    #include <assert.h>
    #include <fcntl.h>
    
    #define NUM_THREAD 4
    #define NUM_LENGTH 200
    #define MAX_EVENTS 20
    
    #define USES_EPOLL
    
    #ifdef USES_EPOLL
    /****
    
    (1).创建一个epoll描写叙述符,调用epoll_create()来完毕,epoll_create()有一个整型的參数size。用来告诉内核,要创建一个有size个描写叙述符的事件列表(集合)
    int epoll_create(int size)
    
    (2).给描写叙述符设置所关注的事件,并把它加入到内核的事件列表中去,这里须要调用epoll_ctl()来完毕。

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 这里op參数有三种,分别代表三种操作: a. EPOLL_CTL_ADD, 把要关注的描写叙述符和对其关注的事件的结构,加入到内核的事件列表中去 b. EPOLL_CTL_DEL,把先前加入的描写叙述符和对其关注的事件的结构,从内核的事件列表中去除 c. EPOLL_CTL_MOD。改动先前加入到内核的事件列表中的描写叙述符的关注的事件 (3). 等待内核通知事件发生。得到发生事件的描写叙述符的结构列表,该过程由epoll_wait()完毕。

    得到事件列表后,就能够进行事件处理了。 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) – EPOLLIN。读事件 – EPOLLOUT,写事件 – EPOLLPRI,带外数据。与select的异常事件集合相应 – EPOLLRDHUP,TCP连接对端至少写写半关闭 – EPOLLERR,错误事件 – EPOLLET,设置事件为边沿触发 – EPOLLONESHOT,仅仅触发一次,事件自己主动被删除 */ int g_epollfd; int g_wakeFds[2]; #endif void awake() { ssize_t nWrite; do { nWrite = write(g_wakeFds[1], "W", 1); } while (nWrite == -1); } void awoken() { char buffer[16]; ssize_t nRead; do { nRead = read(g_wakeFds[0], buffer, sizeof(buffer)); } while ((nRead == -1 ) || nRead == sizeof(buffer)); } using namespace std; void* threadRead(void* userdata) { queue<int>* q = (queue<int>*)userdata; struct epoll_event events[MAX_EVENTS]; while( true ) { int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000); if(fds < 0){ printf("epoll_wait error, exit "); break; } for(int i = 0; i < fds; i++){ if( events[i].events & EPOLLIN ) // read event { printf("%s,%d/%d ", "EPOLLIN",i,fds); while( !q->empty() ) { q->pop(); printf("removed! " ); } } } awoken(); } return userdata; } void* threadRun(void* userdata) { queue<int>* q = (queue<int>*)userdata; while( true ) { #ifdef USES_EPOLL q->push( 1 ); printf("%ld:%s ",(long)pthread_self() ,"added!"); awake(); #else #endif usleep(1000*500); } printf("exit thread:%ld ",(long)pthread_self() ); return userdata; } int main(int argc, char const *argv[]) { /** pipe(建立管道): 1) 头文件 #include<unistd.h> 2) 定义函数: int pipe(int filedes[2]); 3) 函数说明: pipe()会建立管道,并将文件描写叙述词由參数filedes数组返回。

    filedes[0]为管道里的读取端 filedes[1]则为管道的写入端。 */ int result = pipe(g_wakeFds); assert( result!=0 ); result = fcntl(g_wakeFds[0], F_SETFL, O_NONBLOCK); assert(result!=0); result = fcntl(g_wakeFds[1], F_SETFL, O_NONBLOCK); assert(result!=0); g_epollfd = epoll_create( MAX_EVENTS ); assert( g_epollfd > 0 ); struct epoll_event epv = {0, {0}}; //epv.data.ptr = userdata; epv.data.fd = g_wakeFds[0]; epv.events = EPOLLIN; if(epoll_ctl(g_epollfd, EPOLL_CTL_ADD, g_wakeFds[0], &epv) < 0) printf("Event Add failed[fd=%d], evnets[%d] ", epv.data.fd, epv.events); else printf("Event Add OK[fd=%d], op=%d, evnets[%0X] ", epv.data.fd, EPOLL_CTL_ADD, epv.events); queue<int> q; vector<pthread_t> v; for (int i = 0; i < NUM_THREAD; ++i) { pthread_t tid; pthread_create(&tid,NULL,threadRun,&q); v.push_back(tid); } pthread_t tid; pthread_create(&tid,NULL,threadRead,&q); v.push_back(tid); for(vector<pthread_t>::const_iterator it = v.begin(); it < v.end(); ++it) pthread_join(*it,NULL); return 0; }



    大致思路是这种:

    a.127行開始建立管道g_wakeFds,g_wakeFds[0]是读取port。g_wakeFds[1]是写入port

    b.136行创建全局的g_epollfd,即epoll文件描写叙述符,參数为这个文件描写叙述符所支持的最大事件数

    c.144行epoll_ctl创建一个事件关联,即将g_epollfd与g_wakeFds[0]进行关联,假设g_wakeFds[0]发生变化,就会触发事件,而且事件为139创建的epoll_event实例

    d.151-156行创建多个线程作为生产者,生产int放入queue中,放入完后调用awake()函数,向g_wakeFds[1]写入一字节,触发事件

    f.158-160行创建一个消费者来消费生产的int

    g.当中76行int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000);来等待生产者生产的int。当g_wakeFds[1]有数据写入时,g_wakeFds[0]就会触发刚刚注冊的事件。获取到注冊的事件后对事件进行处理(消费int)。随后调用awoken()清空g_wakeFds[0]。进入下一轮epoll_wait


    注意:生产enqueue和消费dequeue是须要同步锁的,这里省略了这个过程。android在java中对Message实现的同步锁


    4 执行结果



    5 源代码下载

    http://pan.baidu.com/s/1i3BTWpv


    6 总结

    当一个线程的消息队列没有消息须要处理时。它就会在这个管道的读端文件描写叙述符上进行睡眠等待,直到其它线程通过这个管道的写端文件描写叙述符来唤醒它。这样就节省了线程上对于cpu资源的消耗。


    7 reference

    《Android系统源代码情景分析》- 罗升阳

    Android NDK 源代码

    Android SDK 源代码



  • 相关阅读:
    求24点
    关于参数和返回值的常量性
    点到平面的距离公式
    大端序与小端序
    Quake3中的绝对值函数
    整数超出范围时如何表示?
    关于数组的几道面试题
    在移位数组中查找数
    时间复杂度O(n),空间复杂度O(1)的排序
    C++之对象切割
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10541738.html
Copyright © 2020-2023  润新知