可epoll队列

可epoll队列.pdf

什么是可epoll队列?

就可以使用epoll来监控队列中是否有数据的队列,当然也支持select和poll。

应用场景

一个线程,需要将队列(共享内存队列或普通队列均可)中的数据取出来,然后通过网络发送出去。如果没有可epoll队列,这个问题处理起来就比较麻烦。

代码实现

实现基于pipe,但pipe可能会产生毛刺。新的内存(2.6.22)引入了eventfd(相关的还有timerfd和signalfd),基于它的实现,不会有毛刺。

/** 可以放入Epoll监控的队列
* RawQueueClass为原始队列类名,如util::CArrayQueue
* 为线程安全类
*/
template <class RawQueueClass>
class CEpollableQueue: public CEpollable
{
typedef typename RawQueueClass::_DataType DataType;
public:
/** 构造一个可Epoll的队列,注意只可监控读事件,也就是队列中是否有数据
* @queue_max: 队列最大可容纳的元素个数
* @exception: 如果出错,则抛出CSyscallException异常
*/
CEpollableQueue(uint32_t queue_max)
:_raw_queue(queue_max)
,_push_waiter_number(0)
{
if (-1 == pipe(_pipefd)) throw sys::CSyscallException(errno, __FILE__, __LINE__);
set_fd(_pipefd[0]);
}
~CEpollableQueue()
{
close();
}
/** 关闭队列 */
virtual void close()
{
sys::LockHelper lock_helper(_lock);
if (_pipefd[0] != -1)
{
// 让CEpollable来关闭_pipefd[0],在CEpollable::close()中将会调用
// before_close,以保持语义总是相同的
CEpollable::close();
//close_fd(_pipefd[0]);
_pipefd[0] = -1;
}
if (_pipefd[1] != -1)
{
close_fd(_pipefd[1]);
_pipefd[1] = -1;
}
}
/** 判断队列是否已满 */
bool is_full() const
{
sys::LockHelper lock_helper(_lock);
return _raw_queue.is_full();
}
/** 判断队列是否为空 */
bool is_empty() const
{
sys::LockHelper lock_helper(_lock);
return _raw_queue.is_empty();
}
/***
* 取队首元素
* @elem: 存储取到的队首元素
* @return: 如果队列为空,则返回false,否则返回true
*/
bool front(DataType& elem) const
{
sys::LockHelper lock_helper(_lock);
if (_raw_queue.is_empty()) return false;
elem = _raw_queue.front();
return true;
}
/***
* 弹出队首元素
* @elem: 存储弹出的队首元素
* @return: 如果队列为空,则返回false,否则取到元素并返回true
* @exception: 如果出错,则抛出CSyscallException异常
*/
bool pop_front(DataType& elem)
{
sys::LockHelper lock_helper(_lock);
return do_pop_front(elem);
}
void pop_front()
{
DataType elem;
(void)pop_front(elem);
}
/***
* 从队首依次弹出多个元素
* @elem_array: 存储弹出的队首元素数组
* @array_size: 输入和输出参数,存储实际弹出的元素个数
* @exception: 如果出错,则抛出CSyscallException异常
*/
void pop_front(DataType* elem_array, uint32_t& array_size)
{
uint32_t i = 0;
sys::LockHelper lock_helper(_lock);
for (;;)
{
if (!do_pop_front(elem_array[i])) break;
if (++i == array_size) break;
}
array_size = i;
}
/***
* 向队尾插入一元素
* @elem: 待插入到队尾的元素
* @millisecond: 如果队列满,等待队列非满的毫秒数,如果为0则不等待,直接返回false
* @return: 如果队列已经满,则返回false,否则插入成功并返回true
* @exception: 如果出错,则抛出CSyscallException异常
*/
bool push_back(DataType elem, uint32_t millisecond=0)
{
sys::LockHelper lock_helper(_lock);
while (_raw_queue.is_full())
{
// 立即返回
if (0 == millisecond) return false;
// 超时等待
util::CountHelper<volatile int32_t> ch(_push_waiter_number);
if (!_event.timed_wait(_lock, millisecond))
{
return false;
}
}
char c = 'x';
_raw_queue.push_back(elem);
// write还有相当于signal的作用
while (-1 == write(_pipefd[1], &c, sizeof(c)))
{
if (errno != EINTR)
throw sys::CSyscallException(errno, __FILE__, __LINE__);
}
return true;
}
/** 得到队列中当前存储的元素个数 */
uint32_t size() const
{
sys::LockHelper lock_helper(_lock);
return _raw_queue.size();
}
private:
bool do_pop_front(DataType& elem)
{
// 没有数据,也不阻塞,如果需要阻塞,应当使用事件队列CEventQueue
if (_raw_queue.is_empty()) return false;
char c;
// read还有相当于CEvent::wait的作用
while (-1 == read(_pipefd[0], &c, sizeof(c)))
{
if (errno != EINTR)
throw sys::CSyscallException(errno, __FILE__, __LINE__);
}
elem = _raw_queue.pop_front();
// 如果有等待着,则唤醒其中一个
if (_push_waiter_number > 0) _event.signal();
return true;
}
private:
int _pipefd[2]; /** 管道句柄 */
sys::CEvent _event;
mutable sys::CLock _lock;
RawQueueClass _raw_queue; /** 普通队列实例 */
volatile int32_t _push_waiter_number; /** 等待队列非满的线程个数 */
};

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

IoC+AOP的简单实现

对EnterLib有所了解的人应该知道,其中有一个名叫Policy Injection的AOP框架;而整个EnterLib完全建立在另一个叫作Unity的底层框...

2129
来自专栏Python攻城狮

Django教程(三)- Django表单Form1.Form 基本使用2.Form中字段及插件3.通过Django表单Form来完成需求4.自定义验证验证规则

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

2034
来自专栏名山丶深处

springboot集成jpa

2645
来自专栏安恒网络空间安全讲武堂

PWNCTF部分复现

根据readData和writedata函数的逻辑发现数组是char [22][12],主要是判断越界的if语句有逻辑漏洞

2182
来自专栏小灰灰

Java 动手写爬虫: 二、 深度爬取

第二篇 前面实现了一个最基础的爬取单网页的爬虫,这一篇则着手解决深度爬取的问题 简单来讲,就是爬了一个网页之后,继续爬这个网页中的链接 1. 需求背景 背景...

72810
来自专栏名山丶深处

springboot集成jpa

1677
来自专栏大内老A

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

在本篇文章中,我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契约关注于对服务操作的描...

3925
来自专栏zhisheng

JAVA虚拟机关闭钩子(Shutdown Hook)

当你认真的去看一个组件的源码的时候,你会经常看见这种关闭钩子的函数,如果你不了解的话,谷歌一下,你就会发现如下文章就是搜索引擎出来的第一篇,不愧是出自我们优秀的...

2963
来自专栏Unity

检测Unity客户端无效的预设资源

4、不被代码间接引用,如预设Anniver_01,在代码中是"Anniver_"+number,这种暂时没有好办法解决。

2674
来自专栏坚毅的PHP

my php & mysql FAQ

php中文字符串长度及定长截取问题使用str_len("中国") 结果为6,php系统默认一个中文字符长度为3,可改用mb_strlen函数获得长度,mb_su...

3946

扫码关注云+社区

领取腾讯云代金券