Linux平台上实现队列
转载: http://my.oschina.net/sundq/blog/203600
Linux上目前有两种事件通知方式,一种是线程条件变量,一种是利用eventfd实现事件通知,下面介绍一下利用这两种方法实现异步队列的方法。
线程条件变量
相关函数介绍
- pthread_cond_init:初始化一个线程条件变量。
- pthread_cond_wait:等待条件触发。
- pthread_cond_signal:通知一个线程,线程条件发生。
- pthread_cond_timedwait:等待条件触发,可以设置超时时间。
- pthread_cond_reltimedwait_np:和pthread_cond_timedwait使用基本相同,区别是使用的是相对时间间隔而不是绝对时间间隔。
- pthread_cond_broadcast:通知所有等待线程,线程条件发生。
- pthread_cond_destroy:销毁条件变量。
唤醒丢失问题
如果线程未持有与条件相关联的互斥锁,则调用 pthread_cond_signal() 或 pthread_cond_broadcast() 会产生唤醒丢失错误。满足以下所有条件时,即会出现唤醒丢失问题:
- 一个线程调用 pthread_cond_signal() 或 pthread_cond_broadcast()
- 另一个线程已经测试了该条件,但是尚未调用 pthread_cond_wait()
- 没有正在等待的线程
信号不起作用,因此将会丢失,仅当修改所测试的条件但未持有与之相关联的互斥锁时,才会出现此问题。只要仅在持有关联的互斥锁同时修改所测试的条件,即可调用 pthread_cond_signal() 和 pthread_cond_broadcast(),而无论这些函数是否持有关联的互斥锁。
线程条件变量使用方法
get_resources(int amount)
{
pthread_mutex_lock(&rsrc_lock);
while (resources < amount)
{
pthread_cond_wait(&rsrc_add, &rsrc_lock);
}
resources -= amount;
pthread_mutex_unlock(&rsrc_lock);
}
add_resources(int amount)
{
pthread_mutex_lock(&rsrc_lock);
resources += amount;
pthread_cond_broadcast(&rsrc_add);
pthread_mutex_unlock(&rsrc_lock);
}
eventfd
int eventfd(unsigned int initval, int flags);
eventfd
是Linux提供内核态的事件等待/通知机制,内核维护了一个8字节的整型数,该整型数由 initval
来初始化, flags
参数可以由以下值位或而来:
- EFD_CLOEXEC:设置该描述符的
O_CLOEXEC
标志。 - EFD_NONBLOCK:设置描述符为非阻塞模式。
- EFD_SEMAPHORE:设置描述符为信号量工作模式,在此模式下,
read
模式会使整型数减1并返回数值1。
当内核维护的8字节整型数为0时, read
操作会阻塞,如果为fd设置为非阻塞模式,则返回 EAGAIN
错误。
简单的唤醒队列
下面我们实现一个简单的环形队列:
#define default_size 1024 typedef struct queue
{
int header;
int tail;
int size;
int capcity;
void **_buf;
} queue_t; queue_t *queue_create(int size)
{
queue_t *q = malloc(sizeof (queue_t));
if (q != NULL)
{
if (size > 0)
{
q->_buf = malloc(size);
q->capcity = size;
}
else
{
q->_buf = malloc(default_size * sizeof (void *));
q->capcity = default_size;
}
q->header = q->tail = q->size = 0;
} return q;
} int queue_is_full(queue_t *q)
{
return q->size == q->capcity;
} int queue_is_empty(queue_t *q)
{
return q->size == 0;
} void queue_push_tail(queue_t *q, void *data)
{
if (!queue_is_full(q))
{
q->_buf[q->tail] = data;
q->tail = (q->tail + 1) % q->capcity;
q->size++;
}
} void *queue_pop_head(queue_t *q)
{
void *data = NULL;
if (!queue_is_empty(q))
{
data = q->_buf[(q->header)];
q->header = (q->header + 1) % q->capcity;
q->size--;
}
return data;
} int *queue_free(queue_t *q)
{
free(q->_buf);
free(q);
}
线程变量实现的异步队列
typedef struct async_queue
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int waiting_threads;
queue_t *_queue;
} async_queue_t;
async_queue_t *async_queue_create(int size)
{
async_queue_t *q = malloc(sizeof (async_queue_t));
q->_queue = queue_create(size);
q->waiting_threads = 0;
pthread_mutex_init(&(q->mutex), NULL);
pthread_cond_init(&(q->cond), NULL); return q;
} void async_queue_push_tail(async_queue_t *q, void *data)
{
if (!queue_is_full(q->_queue))
{
pthread_mutex_lock(&(q->mutex));
queue_push_tail(q->_queue, data);
if (q->waiting_threads > 0)
{
pthread_cond_signal(&(q->cond));
}
pthread_mutex_unlock(&(q->mutex));
} } void *async_queue_pop_head(async_queue_t *q, struct timeval *tv)
{
void *retval = NULL;
pthread_mutex_lock(&(q->mutex));
if (queue_is_empty(q->_queue))
{
q->waiting_threads++;
while (queue_is_empty(q->_queue))
{
pthread_cond_wait(&(q->cond), &(q->mutex));
}
q->waiting_threads--;
}
retval = queue_pop_head(q->_queue);
pthread_mutex_unlock(&(q->mutex));
return retval;
} void async_queue_free(async_queue_t *q)
{
queue_free(q->_queue);
pthread_cond_destroy(&(q->cond));
pthread_mutex_destroy(&(q->mutex));
free(q);
}
eventfd实现的异步队列
typedef struct async_queue
{
int efd; //event fd
fd_set rdfds; //for select
queue_t *_queue;
} async_queue_t;
async_queue_t *async_queue_create(int size)
{
async_queue_t *q = malloc(sizeof (async_queue_t)); q->efd = eventfd(0, EFD_SEMAPHORE|EFD_NONBLOCK);
q->_queue = queue_create(size);
FD_ZERO(&(q->rdfds));
FD_SET(q->efd, &(q->rdfds)); return q;
} void async_queue_push_tail(async_queue_t *q, void *data)
{
unsigned long long i = 1;
if (!queue_is_full(q->_queue))
{
queue_push_tail(q->_queue, data);
write(q->efd, &i, sizeof (i));
}
} void *async_queue_pop_head(async_queue_t *q, struct timeval *tv)
{
unsigned long long i = 0;
void *data = NULL;
if (select(q->efd + 1, &(q->rdfds), NULL, NULL, tv) == 0)
{
return data;
}
else
{
read(q->efd, &i, sizeof (i));
return queue_pop_head(q->_queue);
}
} void async_queue_free(async_queue_t *q)
{
queue_free(q->_queue);
close(q->efd);
free(q);
}
总结
两种实现方法线程条件变量比较复杂,但是性能略高,而eventfd实现简单,但是性能略低。
Linux平台上实现队列的更多相关文章
- 如何在linux平台上编译安装zlib软件(公司部分线上机器缺少zlib不能安装supervisor)
文章在Centos 6.5 linux平台上演示一下如何进行编译安装zlib软件,并配置相关的选项加载使用.示范从下载到安装并配置进行使用过程一系列整套讲解,希望可以给网友考虑使用,谢谢. 工具 ...
- Linux平台上轻松安装与配置Domino
Linux平台上轻松安装与配置Domino Domino Server的编译安装过程中需要用到libstdc++-2.9和glibc-2.1.1(或者其更高的版本)两个编译模块,它们是Linux开发编 ...
- 在LINUX平台上手动创建多个实例(oracle11g)
在LINUX平台上手动创建多个实例(oracle11g) http://blog.csdn.net/sunchenglu7/article/details/39676659 ORACLE linux ...
- Jexus是一款Linux平台上的高性能WEB服务器和负载均衡网关
什么是Jexus Jexus是一款Linux平台上的高性能WEB服务器和负载均衡网关,以支持ASP.NET.ASP.NET CORE.PHP为特色,同时具备反向代理.入侵检测等重要功能.可以这样说,J ...
- Linux平台上常用到的c语言开发程序
Linux操作系统上大部分应用程序都是基于C语言开发的.小编将简单介绍Linux平台上常用的C语言开发程序. 一.C程序的结构1.函数 必须有一个且只能有一个主函数main(),主函数的名为main. ...
- [4G]Linux平台上实现4G通信
转自:http://blog.sina.com.cn/s/blog_7880d3350102wb92.html 在ARM平台上实现4G模块的PPP拨号上网,参考网上的资料和自己的理解,从一无所知到开发 ...
- windows平台是上的sublime编辑远程linux平台上的文件
sublime是个跨平台的强大的代码编辑工具,不多说. 想使用sublime完毕linux平台下django网站的代码编辑工作以提高效率(原来使用linux下的vim效率较低,适合编辑一些小脚本). ...
- Domino V8 在 UNIX/Linux 平台上的安装及其常见问题
在 IBM Bluemix 云平台上开发并部署您的下一个应用. 开始您的试用 Domino V8 的安装需求 Domino V8 可以支持多种平台和操作系统,表1 列出了其支持的各种 UNIX/Lin ...
- Linux平台上搭建apache+tomcat负载均衡集群
传统的Java Web项目是通过tomcat来运行和发布的.但在实际的企业应用环境中,采用单一的tomcat来维持项目的运行是不现实的.tomcat 处理能力低,效率低,承受并发小(1000左右).当 ...
随机推荐
- 关于GCC Cygwin MinGW MSYS
[转载]关于Gcc/MinGW/Cygwin/Msys http://blog.sciencenet.cn/blog-778757-616920.html 一.GCC的历史 GCC是一个原本用于Uni ...
- Ubuntu中升极下载4.2内核
http://tech.hexun.com/2015-09-11/179027013.html 从这段话中所表达出的意思可以了解,Linux Kernel 4.3版本已经开始进行,Linus Torv ...
- windows系统上安装与使用Android NDK r8d(一)
什么是NDK? NDK 提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so 和 java 应用一起打包成apk.这些工具对开发者的帮助是巨大的. NDK 集成了交叉编译器,并 ...
- 虚拟机安装android
通过 虚拟机VirtualBox安装Android x86 4.0系统. Android x86是一个致力于让android运行在x86架构机器上的民间组织搞的项目,目前在世界上有很多人加入了它,虽然 ...
- Linux开发环境必备十大开发工具
Linux是一个优秀的开发环境,但是如果没有好的开发工具作为武器,这个环境给你带来的好处就会大打折扣.幸运的是,有很多好用的Linux和开源开发工具供你选择,如果你是一个新手,你可能不知道有哪些工具可 ...
- javascript比较两个时间大小
//var yourtime=document.getElementById('begin_time').value; var yourtime='2010-12-10 11:12'; yourtim ...
- Android(安卓)开发通过NDK调用JNI,使用opencv做本地c++代码开发配置方法 边缘检测 范例代码
以前写过两个Android开发配置文档,使用NDK进行JNI开发,这样能够利用以前已经写好的C++代码. 前两篇博客地址: http://blog.csdn.net/watkinsong/articl ...
- 深度学习Deep learning
In the last chapter we learned that deep neural networks are often much harder to train than shallow ...
- poj2142 The Balance 扩展欧几里德的应用 稍微还是有点难度的
题目意思一开始没理解,原来是 给你重为a,b,的砝码 求测出 重量为d的砝码,a,b砝码可以无限量使用 开始时我列出来三个方程 : a*x+b*y=d; a*x-b*y=d; b*y-ax=d; 傻眼 ...
- [置顶] JDK工具(零)--简要介绍JDK1.6自带的42个工具
Java的开发人员肯定都知道JDK的bin目录中有“java.exe”和“javac.exe”这两个命令行工具, 但并非所有的Java程序员都了解过JDK的bin目录之中其它命令行程序的作用. JDK ...