一个简易的事件队列的实现

第一章:实现队列

我觉得实现一个队列还是比较有意思的事情,采用常见的循环数组实现的方式。

值得注意的是,Item项是void *类型的,也就是说这个队列可以存储任意类型,因为我们其实存储的是指针。(这么描述真的没问题吗?)

queue.h的源代码如下

/**

 * @file    queue.h

 * @brief

 * */

#ifndef QUEUE_H

#define QUEUE_H
#define
MAXQUEUESIZE 1000 typedef enum boolean{False,True} bool; typedef void * Item; typedef struct Queue * QueueADT; QueueADT NewQueue(void); void FreeQueue(QueueADT queue); void EnQueue(QueueADT queue,Item x); Item DeQueue(QueueADT queue); bool QueueIsEmpty(QueueADT queue); bool QueueIsFull(QueueADT queue); int QueueLength(QueueADT queue); Item GetQueueItem(QueueADT queue,int index); #endif

queue.c的源代码如下:

/**

 * @file    queue.c

 * @brief

 */

#include <stdio.h>

#include <stdlib.h>

#include <malloc.h>

#include <assert.h>

#include "queue.h"

struct Queue

{

    Item elements[MAXQUEUESIZE];

    int iHead;

    int iCount;

};

QueueADT NewQueue(void)

{

    QueueADT queue;

    queue = (QueueADT)malloc(sizeof(struct Queue));

    queue->iHead = queue->iCount = 0;

    return (queue);

}

void FreeQueue(QueueADT queue)

{

    free(queue);

}

void EnQueue(QueueADT queue,Item x)

{

    if(QueueIsFull(queue))

    {

        printf("QueueIsFull\n");

        exit(-1);

    }

    queue->elements[(queue->iHead+queue->iCount)%MAXQUEUESIZE] = x;

    queue->iCount++;

}

Item DeQueue(QueueADT queue)

{

    Item result;

    if(QueueIsEmpty(queue))

    {

        printf("QueueIsEmpty\n");

        exit(-1);

    }

    result = queue->elements[queue->iHead];

    queue->iHead = (queue->iHead + 1)%MAXQUEUESIZE;

    queue->iCount--;

    return (result);

}

bool QueueIsEmpty(QueueADT queue)

{

    return (queue->iCount == 0);

}

bool QueueIsFull(QueueADT queue)

{

    return (queue->iCount == MAXQUEUESIZE);

}

int QueueLength(QueueADT queue)

{

     return (queue->iCount);

}

Item GetQueueItem(QueueADT queue,int index)

{

    if(index >= 0 && index < QueueLength(queue))

    {

        return queue->elements[(queue->iHead + index)%MAXQUEUESIZE];

    }

    printf("index < 0 or index > queue length\n");

    exit(-1);

}

第二章:事件队列

事件队列实在队列API上做一个简单的封装。

只是添加了一个DoEvent(QueueADT queue)函数。

Event.h

#ifndef MY_EVENT_H

#define MY_EVENT_H

#include "queue.h"

#define NewEventQueue() NewQueue()

typedef struct Event * Event_ptr;

void FreeEventQueue(QueueADT queue,void(*FreeEventNode)(Event_ptr x));

void EnEventQueue(QueueADT queue,void (* pEvent)(void * argument),void * argument, size_t size);

void DoEvent(QueueADT queue);

#endif // MY_EVENT_H

 

Event.c

#include <assert.h>

#include <stdlib.h>

#include <stdio.h>

#include "Event.h"

#include <string.h>

typedef struct Event

{

    void (* pEvent)(void * argument);

    void * argument;

}Event;

inline static void QuitIfPtrIsNULL(void * ptr,const char * message)

{

    if(ptr == NULL)

    {

        fprintf(stderr,"%s\n",message);

        exit(-1);

    }

}

void FreeEventQueue(QueueADT queue,void(*FreeEventNode)(Event_ptr x))

{

    int i = 0;

    for(i = 0;i < QueueLength(queue);i++)

    {

        FreeEventNode(GetQueueItem(queue,i)); //释放单个节点malloc的内存,由用户实现

    }

    FreeQueue(queue);

}

void EnEventQueue(QueueADT queue,void (* pEvent)(void * argument),void * argument, size_t size)

{

    Event * x = NULL;

    QuitIfPtrIsNULL(queue,"queue == NULL");

    QuitIfPtrIsNULL(pEvent,"pEvent == NULL");

    x = (Event *)malloc(sizeof(Event));

    QuitIfPtrIsNULL(x,"malloc error");

    x->pEvent   = pEvent;

    x->argument = malloc(size);

    memcpy(x->argument,argument,size);

    EnQueue(queue,x);

}

void DoEvent(QueueADT queue)

{

    Event * x = NULL;

    QuitIfPtrIsNULL(queue,"queue == NULL");

    if(!QueueIsEmpty(queue))

    {

        x = DeQueue(queue);

        QuitIfPtrIsNULL(x,"some thing is wrong");

        x->pEvent(x->argument);

        free(x->argument);

        free(x);

    }

}

 

第三章:事件模拟

在完成底层库之后,可以进行事件模拟了,当然,由于在linux下面编程,略微风骚的扩展了下功能,用了定时器,一次检测事件,一次处理事件。

代码如下:

#include <stdio.h>

#include <stdlib.h>

#include <sys/select.h>

#include <sys/time.h>

#include <unistd.h>

#include <signal.h>

#include <termios.h>

#include "queue.h"

#include "Event.h"

#include "kbhit.h"

unsigned int TimerCount;

QueueADT queue;

void PrintChar(void * c)

{

    char * a = (char *)c;

    printf("\n%c",*a);

}

void timefunc(int sig)      /* 定时事件代码 */

{

    if(TimerCount++ % 7 != 0)                //why I am 7,but not 2?

    {

        if(kbhit())

        {

            char c = getch();

            EnEventQueue(queue,PrintChar,&c,sizeof(c));

        }

    }

    else

    {

        DoEvent(queue);

    }

    signal(SIGPROF, timefunc);    /* 捕获定时信号 */

}

int main()

{

    queue = NewQueue();

    struct itimerval value;

    value.it_value.tv_sec       = 0;    // 定时1.5秒

    value.it_value.tv_usec      = 100000;

    value.it_interval.tv_sec    = 0;    // 定时1.5秒

    value.it_interval.tv_usec   = 100000;

    signal(SIGPROF, timefunc);     // 捕获定时信号

    setitimer(ITIMER_PROF, &value, NULL); // 定时开始

    while (1);

    return 0;

}

 

如上所示,假如TimerCount++ % 2 == 0 那么检测事件的频率和处理事件的频率就一样了,通过调整参数,我们可看到经典讨论下reader和writter问题。

让读者的频率加快,或者让写者的速率加快。

由于linux下面没有kbhit和getch函数,所以照搬了网上的两段代码,如下。

#include <stdio.h>

#include <termios.h>

#include <unistd.h>

#include <fcntl.h>

int kbhit(void)

{

    struct termios oldt, newt;

    int ch;

    int oldf;

    tcgetattr(STDIN_FILENO, &oldt);

    newt = oldt;

    newt.c_lflag &= ~(ICANON | ECHO);

    tcsetattr(STDIN_FILENO, TCSANOW, &newt);

    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);

    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

    ch = getchar();

    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

    fcntl(STDIN_FILENO, F_SETFL, oldf);

    if(ch != EOF)

    {

        ungetc(ch, stdin);

        return 1;

    }

    return 0;

}



int getch(void)

{

    struct termios oldt,newt;

    int ch;

    tcgetattr( STDIN_FILENO, &oldt );

    newt = oldt;

    newt.c_lflag &= ~( ICANON | ECHO );

    tcsetattr( STDIN_FILENO, TCSANOW, &newt );

    ch = getchar();

    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );

    return ch;

}
检验发现,实现的也不是特别好。

你可能感兴趣的:(队列)