编写跨平台的进程内Event事件驱动

来源:互联网 发布:思维空间份额 知乎 编辑:程序博客网 时间:2024/04/28 05:34
       嗯.开头之前呢.我先把我对Event机制简单说一下.
       Event也就是事件,典型的就是Windows的消息,我们写Windows程序时就会经常碰到这种消息:
SendMessage(HWND.....)以及经典的switch(),通过事件机制,进程内部的通信就变得轻松了,相比之下.unix就没有这么方便了.虽然有msgget以及msgsnd之类的消息队列函数,但在win32可没有这类的函数哦.

       所以呢.如果想写一些跨平台的程序,经常就要借助第三方的库比如QT或wxWindow之类的,不过我可不太喜欢一个程序就带一个很大的库,如果是gui还好.如果是命令行的...嘿嘿.你就要装x还有gtk等等,编译几个小时没有问题的. 所以我们就写一个简单的进程内的事件驱动实现,省得带这么大的库.
      
      进程内的事件驱动主要是三部分,一是定义事件,二是准备接收事件.三是响应事件.事件的执行顺序就是按照fifo的原则了,结构当然是选用简单的栈队列了.
typedef struct tagQInn QInn;
typedef struct tagInn Inn;

typedef struct tagQInnVtbl {
        void     (*Push)(QInn *self,void *data);
        void     *(*Pop)(QInn *self);
        void     (*Release)(QInn *self);
}QInnVtbl;

struct tagInn
{
        void *data;
        struct tagInn *pNext;
};

struct tagQInn
{
        Inn *pHead;
        Inn *pTail;
        QInnVtbl *lpVtbl;
};

// 这种结构可以在chamoro.tar.gz中找到.

就提供两个函数.pop以及push,这样就可以实现事件的响应了

另外,我们的进程内事件就叫QEvent吧.我们先看看结构

typedef struct tagQEvent QEvent;


typedef struct QEventVtbl {
        int      (*EventStart) (QEvent *self);
        int      (*AddEvent) (QEvent *self ,int event,void (*eventfuc)(void *));
        int    (*SendEvent) (QEvent *self,int event,void *param);
        int      (*EventRun) (void *param);
        int      (*Release) (QEvent *self);
}QEventVtbl;


//原始的事件列表
typedef struct tagQEventUnit {
        int m_nEventID;
        void (*m_pEvenFunc)(void *);
} QEventUnit;

//在队列中的事件
typedef struct tagQEventDeed {
        int m_nEvent;
        void *m_pParam;
} QEventDeed;

struct tagQEvent {
        QThread *m_threadEvent;

        pthread_mutex_t *m_mutexEvent;
        pthread_cond_t *m_condEvent;
        pthread_mutex_t *m_mutexUnit;

        QInn *m_innEvent;                       //当前的事件列表
        QAutoList *m_listUnit;          //原生的事件,根据事件列表提供的eventid取出事件执行

        QEventVtbl *lpVtbl;
};   

主要是四个函数,我们在后面慢慢的讲

      定义事件一般是事先定义好事件的ID以及响应事件执行的函数,就叫AddEvent吧.把事件的ID比如EN_HELLO先定义好.

      #define EN_HELLO 110

   因为需要把事先定义好事件保存成一个原始的待响应列表,以便有事件到达时可以方便的找出事件,因为我们需要把待响应事件保存起来(具体的QList实现,可以看我另一个作品Chamoro里的QList.我这里就不重复了)

   看看AddEvent的实现

        QEventUnit *unt = NULL;
        assert(self);
        assert(eventfuc);
        unt = (QEventUnit *)malloc(sizeof(QEventUnit));
        if(unt == NULL)
        {
                return -1;
        }
        pthread_mutex_lock(self->m_mutexUnit);
   
        unt->m_nEventID = event;
        unt->m_pEvenFunc = eventfuc;
        self->m_listUnit->list.lpVtbl->Add(&self->m_listUnit->list,unt);
        pthread_mutex_unlock(self->m_mutexUnit);
   
   这里我们讲一下eventfuc这个参数,eventfuc的原型在      int      (*AddEvent) (QEvent *self ,int event,void (*eventfuc)(void *));中的void (*eventfuc)(void *),这是一个指针函数,所以我们的事件响应函数的声明一般的方法是

   void OnHello(void *param);

   待响应事件建立完后就这可以StartEvent了.就是我们可以接收和响应事件了.

   我们看看StartEvent的实现:    

      assert(self);
        self->m_threadEvent->lpVtbl->Create(self->m_threadEvent,self->lpVtbl->EventRun,self);

   就一句话,之所以需要用StartEvent来手工启动接收线程,就是因为我们在还没有AddEvent之前,是不会有事件响应的,所以我们可以灵活一点.手工来启动要不要接收事件.(QThread的实现代码我后面附上)

   StartEvent时就会启动一个线程,这个线程就是self->lpVtbl->EventRun,也就是线程运行的主体,这个函数检查有没有新的事件,有新的事件就调用相应的响应函数.来完成事件驱动.

   讲EventRun之前,我们还是看看int    (*SendEvent) (QEvent *self,int event,void *param);这个函数吧.如果你做过Win32开发,你一定对SendMessage不陌生.呵呵.很熟悉吧.SendEvent的第二个函数就是事件的ID,第三个是参数,如果你多个的参数,你最好用结构来传送.再看看SendEvent的实现吧,代码面前,没有秘密. :)

        QEventDeed *deed = NULL;
        assert(self);

        deed = (QEventDeed *)malloc(sizeof(QEventDeed));
        if(deed == NULL)
        {
                return -1;
        }

        pthread_mutex_lock(self->m_mutexEvent);
        deed->m_nEvent = event;
        deed->m_pParam = param;
        self->m_innEvent->lpVtbl->Push(self->m_innEvent,deed);
        pthread_cond_signal(self->m_condEvent);
        pthread_mutex_unlock(self->m_mutexEvent);

   哈哈,是不是很简单?就一个Push,先生成一个声明,再加到栈中.就这么简单. :) 当然了,还是需要锁的.看到 pthread_cond_signal(self->m_condEvent);这句了吧.等一下我们马上就会知道了,源加完事件.当然要通知主进程,有事件了啦.不要睡了 :P

   OK.马上看EventRun

        QEvent *self = (QEvent *)param;
        QEventUnit *unit;
        QEventDeed *deed;
#ifdef WIN32
// DWORD ThreadID;
#endif
        assert(self);

        while(TRUE)
        {
                pthread_mutex_lock(self->m_mutexEvent);
                // 如果没有新的事件,就等待新事件
                if (!self->m_innEvent->pHead)
                {
                        pthread_cond_wait(self->m_condEvent,self->m_mutexEvent);
                        pthread_mutex_unlock(self->m_mutexEvent);

                        continue;
                }
                else
                {
                        pthread_mutex_lock(self->m_mutexUnit);
                        // 如果有新的事件,查找事件相对应的event
                        deed = (QEventDeed *)self->m_innEvent->lpVtbl->Pop(self->m_innEvent);
                        //找出事件相对应的id
                        self->m_listUnit->list.lpVtbl->MoveToHead(&self->m_listUnit->list);
                        unit = (QEventUnit *)self->m_listUnit->list.lpVtbl->GetData(&self->m_listUnit->list);
                        while(unit)
                        {
                                if(unit->m_nEventID == deed->m_nEvent )
                                {
                                        pthread_mutex_unlock(self->m_mutexEvent);
                                        pthread_mutex_unlock(self->m_mutexUnit);

                                        //创建线程还是直接执行回调函数
                                        unit->m_pEvenFunc(deed->m_pParam);

                                        pthread_mutex_lock(self->m_mutexEvent);
                                        pthread_mutex_lock(self->m_mutexUnit);

                                /*      
#ifdef WIN32
_beginthreadex(0, 0,(unsigned (__stdcall *)(void*))unit->m_pEvenFunc, deed->m_pParam, 0, &ThreadID);
#else
pthread_create( NULL, NULL,unit->m_pEvenFunc, deed->m_pParam);
#endif
                                */
                                        break;
                                }
                                self->m_listUnit->list.lpVtbl->MoveNext(&self->m_listUnit->list);
                                unit = (QEventUnit *)self->m_listUnit->list.lpVtbl->GetData(&self->m_listUnit->list);
                        }
                        free(deed);deed = NULL;
                        pthread_mutex_unlock(self->m_mutexUnit);

                }
                pthread_mutex_unlock(self->m_mutexEvent);
        }
        return 0;

    嗯.好像我的注释已经写得很清楚了.不过有一点,也就是我用/**/注释的那一部分. 

    //创建线程还是直接执行回调函数 

    这点也是我最拿不好了.一般执行回调函数的时候,就会阻塞的,那就会影响到其他函数了,如果用线程的话.代价又会太高,

    因为我的水平有限.暂时没有想出好的解决方法.如果你有好的方法,你告诉我一下吧.

    OK.我们就介绍到这里,不过我们还是要看看如何调用 :)

#include <QEvent.h>


#define EN_HELLO 100

void OnHello(void *param)
{
        char *str = (char *) param;

        if(str == NULL)
                return ;
        printf("OnHello[%s]/n",str);
}
   

int main(int argc,char *argv[])
{

        int i = 10;
        QEvent *event = MallocQEvent();

event->lpVtbl->AddEvent(event,EN_HELLO,OnHello);

event->lpVtbl->EventStart(event);

event->lpVtbl->SendEvent(event,EN_HELLO,"This is Hello Event");
Sleep(1000);
        event->lpVtbl->SendEvent(event,EN_HELLO,"测试中");
        event->lpVtbl->SendEvent(event,EN_HELLO,"测试完");
        event->lpVtbl->WaitQuitEvent(event); //如果你这时发送一个WM_EVENT_OVER的事件就会结束等待了        

//这一行就是WIN sdk 里的那一段,只不过是触发WM_QUIT这个消息罢了

// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) 
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

只不过,我们不是检测有没有消息,而且等着别人来告诉你,你要执行什么事了. :)


OK.未了不要忘记这句哦.
event->lpVtbl->Release(event);
   

}

写完了.这是我第一次写这样的文章,可能写得不是很清楚.如果你们没有看明白的,我等下把代码上传之后.你们看看代码就可以了.另外,QThread我已经进行了封装了. pthread_mutex_unlock 之类的函数我也封装了,不过是封装了win32,原样还是用unix的. :)

Bemusedgod

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 屁股坐久了痛怎么办 坐多了屁股痛怎么办 上班坐的屁股痛怎么办 膝关节手术后康复膝盖疼怎么办 上下楼膝关节疼膝盖疼怎么办 跑步后关节疼该怎么办 后滚翻翻不过去怎么办 走太多路小腿疼怎么办 踢毽子以后期盖右腿内彻疼怎么办 大学体育选修课挂了怎么办 当天贴的砖踩了怎么办 刚贴的瓷砖踩了怎么办 长胶底板太轻怎么办 乒乓球拍胶皮不粘了怎么办 乒乓球拍子胶片太滑怎么办 新买的包黏黏的怎么办 卫星锅收不到台怎么办 养殖厂被卫星拍住怎么办 中六卫星无信号怎么办 晒出成片的斑怎么办 太阳晒出胳膊上长斑怎么办 宇航员在太空死后怎么办 太阳暴晒起的斑怎么办 太阳晒出来的斑怎么办 被认定D级危房怎么办 突然发现两个关系遥远怎么办 如果没有地球人类会怎么办 小锅盖被屏蔽了怎么办 美的冰箱故障通讯不合格怎么办 美的冰箱通讯不合格怎么办 文明6金币降到0怎么办 紫癜肾炎长期尿潜血怎么办 肾移植后血压高怎么办 尿道长了个肿瘤怎么办 吃了有病的鹅怎么办 狗狗得了乳腺瘤怎么办 孕28周还是臀位怎么办 怀孕五个月胎位不正怎么办 33周了胎位不正怎么办 足月胎儿不足5斤怎么办 绒癌观察期怀孕怎么办