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

类别:编程语言 点击:0 评论:0 推荐:
       嗯.开头之前呢.我先把我对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,"测试完");
        

while(i<=1)

{

    Sleep(1000);

    i--;

}

//其实程序写到这里,还有一个大大的问题,就是为什么要用while?呵呵.因为我们的事件驱动需要长期的运行,这一段就是WIN sdk 里的那一段,

// 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

 

本文地址:http://com.8s8s.com/it/it25877.htm