天天看点

EasyDarwin开源流媒体服务器高性能设计之无锁队列

本文来自EasyDarwin团队Fantasy(fantasy(at)easydarwin.org)

一. EasyDarwin任务队列实现

EasyDarwin的任务队列是通过OSQueue类来组织的,操作方法有

EnQueue()插入一个节点

DeQueue()弹出一个节点

外面再经过一层封装,OSQueue_Blocking(),该类对OSQueue的操作都是加锁的,线程池的线程都是各自维护自己的队列,EventContext主循环往里投递事件,线程读取,处理。一边写一边读理论上来讲不需要加锁,但是这里加锁了。

原因是这样的,我们看EnQueue和DeQueue的实现,队列的前后两个队列之间是有联系的,有fNext和fPrev的关系,如果在读的时候,删除了fNext节点,就会出现空指针访问,导致段错误,这个就像是STL迭代器失效。

二. 针对一读一写去锁问题

大家可以学习一下内核的RCU机制,http://blog.csdn.net/nevil/article/details/7718375,通过缓存多份内存的方法避免读写冲突,举个很简单的例子,两个进程需要访问同一片共享内存,为了提高效率,我们不加锁(因为这两个进程都不能阻塞),A会定时更新共享内存,B会查询内存中的数据,A更新内存的操作不是原子操作,也就是说可能出现A更新一半,或者还没更新刚刚清空内存的时候,B来查询了,这样就会出现错误的结果。于是我们申请两块内存,A写的时候写内存块1,B查询内存块2,然后更换这两块内存的角色,这样就不会有冲突了。

三. EasyDarwin无锁队列实现

实现起来也比较简单,定一个合适长度的任务数组,目前定的是64

**#define MAX_QUEUE_ELEMS   64**
CyclicElem elems[MAX_QUEUE_ELEMS];
           

实现和OSQueue一模一样的接口

GetLength()获取有效任务个数

EnQueue()插入任务

DeQueue()弹出任务

int iread_pos;
int iwrite_pos;
int ivalid_elems; //定义读写位置,和有效元素个数变量。
           

初始化的时候都是0,如果处理的速度大于插入的速度,将不会有冲突,当读的速度小于写的速度的时候(这种情况下,我们认为队列超载,将丢弃任务),因此实现的时候,只要判断当写位置下一个位置就是读的位置的时候,我们认为写已经超过读一圈了,任务队列已满。具体实现在OSQueue.h中。

获取更多信息

邮件:[email protected]

WEB:www.EasyDarwin.org

Copyright © EasyDarwin.org 2013-2016

EasyDarwin开源流媒体服务器高性能设计之无锁队列

继续阅读