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