首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

消息队列的设计与实现

消息队列的设计与实现

简介消息驱动机制是 GUI 系统的基础,消息驱动的底层基础设施之一是消息队列,它是整个 GUI 系统运转中枢,本文介绍了一个基于环形队列的消息队列实现方法,给出了它的数据结构、主要操作流程和核心代码。
环形队列环行队列是一种首尾相连的队列数据结构,遵循先进先出原则,如下图所示:
  
ring buffer 示意图
在环形队列中用一组连续地址的存储单元依次存放从队列头到队列尾的元素,通过两个指针 read_pos 和 write_pos 分别指向读取位置和写入位置。
初始化队列时,令 read_pos = write_pos = 0,每当写入一个新元素时,write_pos 增 1;每当读取一个元素时,read_pos 增 1 。若队列已满,不能往队列写入数据;若队列为空,则不能读取数据。判断对列是否为满的的方法是看(write_pos + 1)% QUEUE_SIZE == read_pos 是否成立,判断队列是否为空的方法是看 write_pos == read_pos 是否成立。
鉴于多个线程同时访问环形队列,需要考虑线程之间的互斥和同步问题,拟采用锁控制多个线程互斥访问环形队列,使用信号量控制线程之间的同步。
一段时间内只能有一个线程获得锁,当它持有锁时,其它线程要访问环形队列必须等待,直到前者释放锁。由此,锁可以保证多个线程互斥的访问环形队列。
线程从队列对数据前首先判断信号量是否大于 1 ,若是,则从队列读数据;否则,进入等待状态,直到信号量大于 1 为止;线程往队列写入一个数据后,会将信号量增 1 ,若有线程在等待,则会被唤醒。由此,信号量实现了多线程同步访问环形队列。
流程图下图是环形缓冲区的初始化、读数据、写数据的主要流程。
  
ring buffer 流程图
  • 初始化时为环形队列分配内存空间,并完成锁和信号量的初始化;
  • 若往环形队列写数据,首先要获得锁, 若锁已被占用,则进入等待状态,否则进一步去判断环形队列是否已满。若满了,则释放锁并返回;若队列未满,将数据写入 write_pos 位置,write_pos 增 1,释放锁并将信号量增 1,表示已写入一个数据;
  • 若从环形队列读数据,首先判断信号量是否大于 1 ,若不是,则等待,否则去获取锁,若锁已被占用,则等待,否则从 read_pos 位置读取数据,将read_pos 增 1 ,释放锁,读取完毕。
数据结构环形队列的数据结构如下所示:
typedef _MSG {
    int message;
    void* param;
} MSG;

typedef _MSGQUE {
    pthread_mutex_t lock;
    sem_t  wait;

    MSG* msg;
    int size;

    int read_ops;
    int write_ops;
} MSGQUEUE;


环形队列包括如下数据:
  • lock:互斥锁;
  • wait:信号量
  • msg:指向数据区的指针;
  • size:环形队列数据最大个数;
  • read_ops:读取位置;
  • write_ops:写入位置。
队列初始化初始化主要完成三个任务:
  • 为环形队列分配内存;
  • 初始化互斥锁,用 pthread_mutex_init 完成;
  • 初始化信号量,用 sem_init 完成。
/* Create message queue */
_msg_queue = malloc (sizeof (MSGQUEUE));

/* init lock and sem */
pthread_mutex_init (&_msg_queue->lock, NULL);
sem_init (&_msg_queue->wait, 0, 0);

/* allocate message memory */
_msg_queue -> msg = malloc (sizeof(MSG) * nr_msg);
_msg_queue -> size = nr_msg;
继承事业,薪火相传
返回列表