项目中的Libevent(多线程)
多线程版Libevent
//保存线程的结构体
struct LibeventThread
{
LibEvtServer* that; //用作传参
std::shared_ptr<std::thread> spThread; // 线程
struct event_base * thread_base; // 事件根基
struct event notify_event;
evutil_socket_t notfiy_recv_fd; // socketpair 接收端fd(工作线程接收通知)
evutil_socket_t notfiy_send_fd; // socketpair 发送端fd(监听线程发送通知)
#ifdef BOOST_LOCKFREE
boost::lockfree::spsc_queue<conn_queue_item, boost::lockfree::capacity<> > conn_queue;
#else
std::mutex conn_mtx; //维护连接队列的锁
std::queue<conn_queue_item> conn_queue; //conn_queue 是一个管理conn_queue_item的队列
#endif
}; bool LibEvtServer::init(I_NetServerEvent* event, int start, int size)
{
m_ids = new ChannelIDGenerator();
m_ids->init(start, size);
m_allChannels.resize(m_ids->getSize()); m_event = event; //event支持windows下线程的函数
int hr = evthread_use_windows_threads();
m_base = event_base_new();
if (!m_base) {
fprintf(stderr, "Could not initialize libevent!\n");
return false;
}
#ifdef MUL_LIBEVENT_THREAD
m_last_thread = -; //注意初始化为-1
//初始化线程
init_threads(THREAD_NUMB);
#endif
return true;
} bool LibEvtServer::init_threads(int thread_numb)
{
m_libevent_threads.resize(thread_numb); //为每个线程指定双向通道(类似于管道)
for(int i = ; i < thread_numb; ++i)
{ LibeventThread* plt = new LibeventThread();
#ifdef WIN32
//创建一个socketpair即可与互相通信的两个socket,保存在fds里面
evutil_socket_t fds[];
if(evutil_socketpair(AF_INET, SOCK_STREAM, , fds) < )
{
std::cout << "创建socketpair失败\n";
return false;
}
//设置成无阻赛的socket
evutil_make_socket_nonblocking(fds[]);
evutil_make_socket_nonblocking(fds[]);
#else
int fds[];
if (pipe(fds)) {
perror("Can't create notify pipe");
exit();
}
#endif
plt->notfiy_recv_fd = fds[];
plt->notfiy_send_fd = fds[]; //安装libevent线程[创建base,注册通道事件(用于监听新链接)]
setup_libevent_thread(plt); //线程放入容器中
m_libevent_threads[i] = plt;
} //开始创建并启动线程
for(int i = ; i < thread_numb; ++i)
{
m_libevent_threads[i]->spThread.reset(new std::thread([]
(void* arg)
{
auto me = (LibeventThread*) arg;
// Wait for events to become active, and run their callbacks.
//This is a more flexible version of event_base_dispatch().
event_base_loop(me->thread_base, );
}, m_libevent_threads[i]));
}
return true;
} //设置线程信息
void LibEvtServer::setup_libevent_thread(LibeventThread * pLibeventThread)
{
auto plt = pLibeventThread;
plt->thread_base = event_base_new(); // 创建线程的event_base //给每个libevent线程设置连接通知回调函数。
plt->that = this;
//设置线程事件notify_event
event_set(&plt->notify_event, plt->notfiy_recv_fd,//EV_READ表示只要这个socket可读就调用notify_cb函数
EV_READ | EV_PERSIST, ::notify_cb, plt);
//设置事件和event_base的关系
event_base_set(plt->thread_base, &plt->notify_event); // 设置事件的从属关系(相当于指明事件属于哪个event_base)
//添加事件
event_add(&plt->notify_event, ); // 正式添加事件
} void LibEvtServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
#ifdef MUL_LIBEVENT_THREAD
int cur_thread = (m_last_thread + ) % THREAD_NUMB; // 轮循选择工作线程
m_last_thread = cur_thread; conn_queue_item item;
item.fd = fd;
//item.ch2 = NULL; auto plt = m_libevent_threads[cur_thread];
{
//向线程的队列中放入一个item,每个线程有个队列,保存连接的socketfd
#ifdef BOOST_LOCKFREE
while(!plt->conn_queue.push(item))
{
#ifndef _DEBUG
boost::this_thread::interruptible_wait();
#else
Sleep();
#endif
Plug::PlugMessageBox("连接队列居然满了,超过1000的未处理数!");
}
#else
std::lock_guard<std::mutex> lock(plt->conn_mtx);
plt->conn_queue.push(item);
#endif
}
//激活读线程的读事件
send(plt->notfiy_send_fd, "c", , ); #else
auto base = evconnlistener_get_base(listener); auto bev = bufferevent_socket_new(base, fd, BEV_OPT_THREADSAFE);//|BEV_OPT_CLOSE_ON_FREE);
if (!bev)
{
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return ;
} auto c2 = CreateChannel(bev); bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, c2);
bufferevent_enable(bev, EV_READ | EV_WRITE ); #endif
} //侦听端口,-1表示向系统申请一个任意可用端口
bool LibEvtServer::listen(int* port)
{
struct sockaddr_in sin; memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET;
if(- == *port)
sin.sin_port = htons();
else
sin.sin_port = htons(*port); m_listener = evconnlistener_new_bind(m_base, ::listener_cb, (void*)this,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -,
(struct sockaddr*)&sin,
sizeof(sin));
if (!m_listener)
{
return false;
} if( - == *port)
*port = ntohs(sin.sin_port); if (!m_listener) {
fprintf(stderr, "Could not create a listener!\n");
return false;
}
m_spListenThread.reset(new std::thread([this]//现在看这个线程只是收到连接,然后交给线程,然后通知线程
{
//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
//event_base_loop(m_base, EVLOOP_ONCE);
event_base_dispatch(m_base);
if(WSAENOTSOCK == WSAGetLastError())
{
Plug::PlugMessageBox(L"操作无效套接字啊!");
} Plug::PlugMessageBox(L"Libevent派发线程退出!");
}));
return true;
} void LibEvtServer::notify_cb(evutil_socket_t fd, short which, LibeventThread *pLibeventThread)
{
//首先将socketpair的1个字节通知信号读出(这是必须的,在水平触发模式下如果不处理该事件,则会循环通知,直到事件被处理)
char buf[];
recv(fd, buf, , );//从sockpair的另一端读数据 auto plt = pLibeventThread; conn_queue_item item; //从自己的连接队列中取出连接数
{
//取出队列中的第一个元素
#ifdef BOOST_LOCKFREE
while(!plt->conn_queue.pop(item))//pop一个出来
{
#ifndef _DEBUG
boost::this_thread::interruptible_wait();
#else
Sleep();
#endif
Plug::PlugMessageBox("通知队列居然弹空了啊!");
}
#else
std::lock_guard<std::mutex> lck(plt->conn_mtx);
item = plt->conn_queue.front();
#endif
} //创建每个socket的bufferevent
auto bev = bufferevent_socket_new(plt->thread_base, item.fd, BEV_OPT_THREADSAFE); Channel2* c2 = CreateChannel(bev); //设置接收、状态改变 回调函数
bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, c2);
bufferevent_enable(bev, EV_READ | EV_WRITE );
} //看了这个过程就是这个样子的,监听线程接收到连接之后把这个socket丢给Libevent线程,libevent创建bufferevent
//处理相关读和写事件,这个工程通过每个线程的连接队列,然后一个socketpair通知的。这样每个线程就很平均的处理所有的连接事件
//多线程比单线程的是复杂很多,只是这种模式不知道,但bufferevent还是一样的
项目中的Libevent(多线程)的更多相关文章
- 项目中的libevent
单线程libevent模式 项目里面是多线程版的,我先理解下单线程的. //client .调用NGP::init() bool NGP::init(NGPcontext context) { _co ...
- [多线程] Web 项目中,少有涉及到的一次多线程编程的经验
如今框架横行,Spring 已经是非常成熟的容器体系,我们在日常开发 JavaWeb 的工作中,大多已经不需要考虑多线程的问题,这些问题都已经在Spring容器中实现,框架的意义就是让程序员们可以专注 ...
- iOS开发多线程在实际项目中的运用
实际项目开发中为了能够给用户更好的体验,有些延时操作我们都会放在子线程中进行. 今天我们就来聊聊多线程在实际项目中的运用. 我们先来看看多线程的基础知识: 1.多线程的原理: 同一时间,CPU只能处理 ...
- Spring Scope:Web项目中如何安全使用有状态的Bean对象?
Web系统是最常见的Java应用系统之一,现在流行的Web项目多使用ssm或ssh框架,使用spring进行bean的管理,这为我们编写web项目带来了很多方便,通常,我们的controler层使用注 ...
- 近期C#项目中总结
1. 读写文件操作 using (file = new System.IO.StreamReader(inputfile)) { using (outfile = new System.IO.Stre ...
- C#项目中常用到的设计模式
1. 引言 一个项目的通常都是从Demo开始,不断为项目添加新的功能以及重构,也许刚开始的时候代码显得非常凌乱,毫无设计可言.但是随着项目的迭代,往往需要将很多相同功能的代码抽取出来,这也是设计模式的 ...
- 项目中添加Log4J支持
首先,在项目中的classes 中新建立一个log4j.properties文件即可: 在实际编程时,要使Log4j真正在系统中运行事先还要对配置文件进行定义.定义步骤就是对Logger.Append ...
- 在Java项目中整合Scala
Scala是一个运行在Java JVM上的面向对象的语言.它支持函数编程,在语法上比Java更加灵活,同时通过Akka库,Scala支持强大的基于Actor的多线程编程.具有这些优势,使得我最近很想在 ...
- 转:Qt项目中遇到的一些小问题汇总
链接:http://blog.csdn.net/yangyunfeizj/article/details/7082001 作者:GoatYangYang 公司让负责qt界面开发,但是接触qt又不 ...
随机推荐
- Quartz 第六课 CronTrigger(官方文档翻译)
CronTriggers使用的频率比SimpleTrigger跟高.如果需要schedule 中触发Job的方式类似于日历的形式而不是一个确定的是时间间隔,那就需要使用CronTrigger. 对于C ...
- [转]解决win8.1右键菜单出现在左边
1.在控制面板中找到“Tablet PC 设置”窗口,选择“其他”选项卡. 2.在“左右手使用习惯”下,点选“惯用左手”,确定. •如果win8.1的控制面板里找不到Tablet PC 设置 •可以在 ...
- Java Executors(线程池)
Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序 ...
- trident 序列号问题
在使用Storm的trident做流计算开发时,遇到一个诡异的问题: 我继承IPartitionedTridentSpout或者IOpaquePartitionedTridentSpout接口做事务型 ...
- 配置PostgreSQL Streaming Replication集群
运行环境: Primary: 192.168.0.11 Standby: 192.168.0.21, 192.168.0.22 OS: CentOS 6.2 PostgreSQL: 9.1.2 版本以 ...
- 《C++Primer中文版》读书笔记——第1章 开始
istream对象:cin(标准输入对象); ostream对象:cout(标准输出对象) cerr(输出错误和警告) clog(输出一般性信息) 读取数量不定的输入数据,eg , sum=; whi ...
- JavaScript 中undefined,null,NaN的区别
1.类型分析: js中的数据类型有undefined,boolean,number,string,object等5种,前4种为原始类型,第5种为引用类型.var a1;var a2 = true;va ...
- 【风马一族_git_github】git的工作流程
git有三个区域(如图): 基本信息设置 1)设置用户名 git config --global user.name "帐号名" 2)设置用户名邮箱 git config ...
- js设计模式(5)---外观模式
0.前言 早上好,今天天气不错,估计有35度吧,坐在空调室里相当惬意,那么酒足饭饱之后就应该干些正事了. 1. 为什么使用外观模式 外观模式提供了一个高层接口,封装一些复杂操作或繁琐行为,方便调用.门 ...
- 用泛型的IEqualityComparer<T>接口去重复项
提供者:porschev 题目:下列数据放在一个List中,当ID和Name都相同时,去掉重复数据 ID Name 1 张三 1 李三 1 小伟 1 李三 2 李四 2 李武 ----- ...