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左右).当 ...
随机推荐
- Linux下OOM Killer机制详解
http://www.cnblogs.com/ylqmf/archive/2012/11/05/2754795.html http://wuquan-1230.blog.163.com/blog/st ...
- 错误 1 error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use . See online help for details.
出现这种警告的原因是因为我们没有使用安全的字符串处理函数.如果想屏蔽这种警告,可以使用: 还可以使用其它的方法,参考: https://www.cnblogs.com/gb2013/archive/2 ...
- 分享开源 Markdown 编辑器 Mditor 的「桌面版」
简单说明 Mditor 最早只有「组件版」,随着「桌面版」的发布,Mditor 目前有两个版本: 可嵌入到任意 Web 应用的 Embed 版本,这是一桌面版的基础,Repo: https://git ...
- ORDER BY,GROUP BY 和DI STI NCT 优化
读<MySQL性能调优与架构设计>笔记之ORDER BY,GROUP BY 和DI STI NCT 优化 2015年01月18日 18:51:31 lihuayong 阅读数:2593 标 ...
- Java:Object类详解
Java的一些特性会让初学者感到困惑,但在有经验的开发者眼中,却是合情合理的.例如,新手可能不会理解Object类.这篇文章分成三个部分讲跟Object类及其方法有关的问题. 上帝类 问:什么是Obj ...
- JAVA , TOMCAT , AXIS2 环境变量配置
想要成功配置Java的环境变量,那肯定就要安装JDK,才能开始配置的. 安装JDK 向导进行相关参数设置.如图: 正在安装程序的相关功能,如图: 选择安装的路径,可以自定义,也可以默认路径.如图: 成 ...
- 【Linux】shell字符串分割、数组访问、条件判断
参考资料: shell字符串分割再循环:http://www.shangxueba.com/jingyan/1633455.html linux shell中 if else以及大于.小于.等于逻辑表 ...
- Zeppelin原理简介
Zeppelin是一个基于Web的notebook,提供交互数据分析和可视化.后台支持接入多种数据处理引擎,如spark,hive等.支持多种语言: Scala(Apache Spark).Pytho ...
- USD Series: Installation
Today I would like to talk is Unified Service Desktop. It was published in Dynamics CRM 2013 Spring ...
- iOS核心动画 - CALayer
大家知道,在iOS中所有的视图都继承自UIView. UIView处理所有的触摸事件和画图. 事实上,UIView所有的渲染和动画是托管给另一个类来负责的,它就是CALayer. 但是,需要记住的是, ...