今天看到一篇介绍环形消息队列的文章,学习了
2013-03-19 10:13
453 查看
消息队列的设计与实现 作者: 刘鹏 日期: 2008-09-25 本文介绍了 GUI 系统中消息队列的设计与实现方法。 |
简介 消息驱动机制是 GUI 系统的基础,消息驱动的底层基础设施之一是消息队列,它是整个 GUI 系统运转中枢,本文介绍了一个基于环形队列的消息队列实现方法,给出了它的数据结构、主要操作流程和核心代码。 环形队列 环行队列是一种首尾相连的队列数据结构,遵循先进先出原则,如下图所示:
初始化队列时,令 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 ,若有线程在等待,则会被唤醒。由此,信号量实现了多线程同步访问环形队列。 流程图 下图是环形缓冲区的初始化、读数据、写数据的主要流程。
若往环形队列写数据,首先要获得锁, 若锁已被占用,则进入等待状态,否则进一步去判断环形队列是否已满。若满了,则释放锁并返回;若队列未满,将数据写入 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; 写操作 如上面的流程图介绍,写操作主要包括如下几步: - 获取锁; 判断队列是否已满; 若没满,将数据写入 write_pos 处,将 write_pos 增 1,并判断 write_pos 是否越界; 释放锁,并将信号量增 1。 /* lock the message queue */ pthread_mutex_lock (_msg_queue->lock); /* check if the queue is full. */ if ((_msg_queue->write_pos + 1)% _msg_queue->size == _msg_queue->read_pos) { /* Message queue is full. */ pthread_mutex_unlock (_msg_queue->lock); return; } /* write a data to write_pos. */ _msg_queue -> msg [write_pos] = *msg; write_pos ++; /* check if write_pos if overflow. */ if (_msg_queue->write_pos >= _msg_queue->size) _msg_queue->write_pos = 0; /* release lock */ pthread_mutex_unlock (_msg_queue->lock); sem_post (_msg_queue->wait); 读操作 同理,读操作分如下几个步骤: 检查信号量; 获取锁; 判断队列是否为空; 若不为空,则读取 read_ops 处的数据,将 read_ops 增 1,并判断 read_pos 是否越界; 并释放锁。 sem_wait (_msg_queue->wait); /* lock the message queue */ pthread_mutex_lock (_msg_queue->lock); /* check if queue is empty */ if (_msg_queue->read_pos != _msg_queue->write_pos) { msg = _msg_queue->msg + _msg_queue->read_pos; /* read a data and check if read_pos is overflow */ _msg_queue->read_pos ++; if (_msg_queue->read_pos >= _msg_queue->size) _msg_queue->read_pos = 0; return; } /* release lock*/ pthread_mutex_unlock (_msg_queue->lock); 问题 本文采用的环形队列是固定长度的,还可进一步改进,设计成可变长度的环形队列; 本文的消息队列是“先进先出”原则,没有考虑带优先级的消息,但这种场合是存在的; 本文重点介绍了消息队列的原理和实现,对于一个 GUI 程序来讲,还需要一个消息循环与消息队列一起工作,消息循环将单独总结。 |
相关文章推荐
- 今天看到的关于深度学习的一篇文章,可以好好学习下
- 今天突然看到一篇介绍WIN98的文章,才发现原来真的老了
- 今天看到一篇很好的文章,介绍了html5常用几种api
- 今天上午看到一篇非常好的,介绍 抽象与接口区别的文章,特别收藏
- 今天上午看到一篇非常好的,介绍 抽象与接口区别的文章,特别收藏
- 今天看到一篇文章,觉得甚有道理
- 很久没写BLOG了,今天在重庆,公司下班了,发一篇,刚刚看到的文章,觉得很好
- 今天看到一篇文章,收藏了很多大牛的博客
- 今天看到一篇关于DSL(Domain Specific Language)的文章
- 程序员有多少喜欢李嘉欣的,今天在程序朋友的一篇博客看到这样的文章,使我大跌眼镜.......
- 看到一篇文章分享给正在或者开始学习前端技术的你
- 学习javascript时从网上看到的一篇比较简单和基础的文章,特转载!
- 【转帖】今天看到一篇有意思的文章:程序员的十楼层。看看自己在第几层
- 看到的一篇介绍strncpy()函数比较好的文章
- 今天看到一篇文章,收藏了很多大牛的博客,在这里分享一下
- 今天看到的一篇文章,标题是"开发人员间的效率差在哪里?",读后有感
- 看到一篇好的文章计算机专业的学习心得
- 转载一篇老外的消息队列服务文章
- 今天看到一篇介绍C#基础概念的,不错收藏了(转)
- 【Android】数据存储数据库SQLite(之前有看到的一篇关于SQLite文章,简单明了、覆盖较全面适合学习)