嗯.开头之前呢.我先把我对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

|