libev IO观察器调用stop后关闭底层FD仍报错如何解决
错误根因
libev默认不提供线程安全保障,除了ev_async_send等极少数明确标注可跨线程调用的接口外,所有对ev_loop、ev_watcher的操作都必须在ev_run执行的线程内完成。你在thread2直接调用IO观察器的stop()方法时,操作没有和thread1正在运行的ev_loop逻辑同步,此时ev_loop内部的epoll逻辑可能已经将该观察器对应的fd纳入待操作列表,stop()还未真正完成移除逻辑就执行了close(fd),最终触发epoll_ctl调用时的无效fd断言。
疑问解答
是不是添加到ev_loop的IO观察器,即便处于停止状态,也必须持有有效(已打开)的FD?
不需要。已经成功完成停止流程的IO观察器,libev不会再对其关联的fd发起任何系统调用,你可以自由关闭fd,甚至复用该观察器绑定其他新的fd。你遇到的问题是停止操作未执行完成就提前关闭了fd,和停止状态的观察器本身无关。
解决方案
最稳妥的方案是通过ev_async观察器实现跨线程通知,将停止观察器、关闭fd的逻辑全部放到ev_run所在线程执行:
- 初始化ev_async观察器并注册到你的ev_loop中,在异步回调中执行清理逻辑
- thread2需要关闭fd时,仅调用
ev_async_send触发异步回调即可,libev保证该接口是线程安全的
示例代码
// 初始化阶段,和其他观察器一起注册async观察者 ev::async myAsync; myAsync.set<MyClass, &MyClass::asyncCleanup>(this); myAsync.set(myEVLoop); myAsync.start(); // 异步回调函数,固定在thread1(ev_run所在线程)执行 void MyClass::asyncCleanup(ev::async &w, int revents) { myEVIO.stop(); close(fd); // 可自行添加标志位避免重复执行清理逻辑 } // thread2中需要关闭fd时,仅需调用以下接口触发回调 myAsync.send();
如果必须要在非ev_run线程操作观察器,可以通过ev_set_loopcb给ev_loop设置锁回调,让libev内部所有操作loop的逻辑都自动加锁,跨线程调用stop()时也持有同一把锁保证操作原子性,该方案复杂度更高,非必要不推荐。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

