必须用event_base_dispatch()或event_base_loop()启动事件循环,不可手动while(1)轮询;前者交由内核通知就绪事件,避免CPU满载、连接无响应等问题。

Libevent事件循环必须用event_base_dispatch()启动,不能直接写while(1)
很多人误以为自己手动轮询event_base_loop()或写个死循环就能处理连接,结果CPU跑满、连接不响应。Libevent的事件驱动本质是让内核通知你“哪个socket就绪了”,而不是你去查——所以必须交由event_base_dispatch()或event_base_loop()接管调度。
常见错误现象:accept()成功但后续read()永远收不到数据;或者连接数一多,新连接被丢弃。
-
event_base_dispatch()运行一次后退出,适合单次服务场景;生产环境一般用event_base_loop(base, 0) - 启动前务必调用
event_base_set_flag(base, EVBASE_FLAG_NOLOCK)(若确定单线程使用),避免内部锁开销 - 不要在回调里长时间阻塞(比如同步DNS查询、文件IO),否则整个事件循环卡住
监听Socket要设为非阻塞,且evutil_make_socket_nonblocking()不能漏
Libevent本身不帮你改socket属性。如果监听socket还是阻塞模式,accept()可能挂起整个事件循环——尤其在SYN洪泛或客户端半开连接时。
使用场景:TCP服务器启动阶段,对listen_fd做初始化。
立即学习“C++免费学习笔记(深入)”;
- 先用
socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)创建(Linux),但Windows不支持SOCK_NONBLOCK,必须补evutil_make_socket_nonblocking() - 设置
SO_REUSEADDR(用setsockopt()),避免重启时报Address already in use -
listen()之后,再用event_new(base, listen_fd, EV_READ|EV_PERSIST, accept_cb, arg)注册,并event_add()
bufferevent比裸event更适合业务逻辑,但得配好回调和水位
直接用event处理读写太底层:你要自己管理recv buffer、判断EAGAIN、拆包粘包。而bufferevent封装了缓冲区、自动重试、EOF检测,是高并发Socket的实际主力。
容易踩的坑:回调没设全,或水位值不合理导致吞吐骤降。
- 创建后立刻调用
bufferevent_setcb(bev, read_cb, write_cb, event_cb, arg),三个回调缺一不可 - 用
bufferevent_setwatermark(bev, EV_READ, low_water, high_water)控制触发频率;例如low_water=1(有数据就读),high_water=65536(写缓存快满时暂停读) - 读回调里别直接
bufferevent_read()——应该用evbuffer_drain()或evbuffer_remove()从bufferevent_get_input(bev)取数据 - 出错或断连时,
event_cb里必须调用bufferevent_free(bev),否则内存泄漏
多线程下event_base不能共享,每个线程一个更稳妥
Libevent官方明确说event_base不是线程安全的。有人把同一个base传给多个线程,结果event_add()随机失败、回调丢失、甚至core dump。
性能影响:单event_base + 多线程worker(如用线程池处理业务)是常见误区;真正高效的是“一个线程一个event_base”,用event_base_loop()独占CPU核。
- 用
pthread_create()前,每个线程调用event_base_new()新建自己的base - 监听socket可以复用(通过
evconnlistener_new_bind()),但连接级的bufferevent必须绑定到对应线程的base - 跨线程投递事件要用
event_active()+event_base_loopbreak()配合,别直接event_add()
bufferevent的错误传播机制——event_cb里what & BEV_EVENT_ERROR出现时,errno值已经不是当前线程的,得靠bufferevent_get_connected()和日志上下文来定位真实原因。


















