聊聊filezilla 3.7.2 事件处理机制
filezilla 3.7.2 处理事件依赖于一个自己封装的事件处理器。名字叫event_loop类。 这个类在libfillezilla工程种。作为filezilla的一个动态库。因为这个处理器对于filezilla十分重要。所以借此机会学习以下。
event_loop 头文件在 libfilezilla工程目录\lib\libfilezilla\event_loop.hpp。 cpp在 libfilezilla\lib\event_loop.cpp。和event_loop关系密切的一个类是event_handler。位置与event_loop相同。 这个类是对event_loop的一个薄层封装。
event_loop 支持两种,一种是事件类(event)的。另外一种是时间类(timer)的。
对于怎么使用 event_loop,libfillezilla中有对应的例子。代码中也有简洁的类。以libfilezilla工程 demo_events 为例。demo_events工程 只有一个叫 events.cpp 的文件。代码十分简单。如下
1 // Define a new event. 2 // The event is uniquely identified via the incomplete my_event_type struct and 3 // has two arguments: A string and a vector of ints. 4 struct my_event_type; 5 typedef fz::simple_event<my_event_type, std::string, std::vector<int>> my_event; 6 7 // A simple event handler 8 class handler final : public fz::event_handler 9 { 10 public: 11 handler(fz::event_loop& l) 12 : fz::event_handler(l) 13 {} 14 15 virtual ~handler() 16 { 17 // This _MUST_ be called to avoid a race so that operator()(fz::event_base const&) is not called on a partially destructed object. 18 remove_handler(); 19 } 20 21 private: 22 // The event loop calls this function for every event sent to this handler. 23 virtual void operator()(fz::event_base const& ev) 24 { 25 // Dispatch the event to the correct function. 26 fz::dispatch<my_event>(ev, this, &handler::on_my_event); 27 } 28 29 void on_my_event(std::string const& s, std::vector<int> const& v) 30 { 31 std::cout << "Received event with text \"" << s << "\" and a vector with " << v.size() << " elements" << std::endl; 32 33 // Signal the condition 34 fz::scoped_lock lock(m); 35 c.signal(lock); 36 } 37 };
可以看出,要使用event_loop类,需要实现自己的 event_handler类函数。在自己的event_handler类中需要做以下几件事。
1 定义构造 析构函数
2 定义自己 simple_event 的数据类型 以及这种数据类型的处理函数。
3 定义自己仿函数(或者叫()重载运算符)。
调用也很简单,代码如下。
1 // Start an event loop 2 fz::event_loop l; 3 4 // Create a handler 5 handler h(l); 6 7 // Send an event to the handler 8 h.send_event<my_event>("Hello World!", std::vector<int>{23, 42, 666}); 9 10 // Wait until a signal from the worker thread 11 fz::scoped_lock lock(m); 12 c.wait(lock);
定义 fz::event_loop 类实例,用这个实例构造 handler 实例。调用 handler 的send_event函数函数。
event_loop 类 依赖一个线程池,一个dqueue的队列。dqueue 用来存放发送过来的时间处理器,以及对应的参数。线程池可能是用来运行多个线程。来处理dqueue里面的数据。
event_loop 成员函数都挺重要的。下面一个一个来分析。
首先看构造函数。构造函数有三个。首先看无参构造函数
1 event_loop::event_loop() 2 : sync_(false) 3 , thread_(std::make_unique<thread>()) 4 { 5 thread_->run([this] { entry(); }); 6 }
这个构造函数采用默认异步方式。使用thread默认构造一个指向thread的智能指针。这个thread不是标准库的thread。而是filezilla自己实现的一个thread类。删除一些注释。代码如下
1 class FZ_PUBLIC_SYMBOL thread final 2 { 3 public: 4 #if defined(FZ_WINDOWS) && (defined(__MINGW32__) || defined(__MINGW64__)) 5 typedef uint32_t id; 6 #else 7 typedef std::thread::id id; 8 #endif 9 10 thread() = default; 11 12 ~thread(); 13 14 bool run(std::function<void()> && f); 15 void join(); 16 bool joinable() const; 17 static id own_id(); 18 19 private: 20 class impl; 21 friend class impl; 22 impl* impl_{}; 23 };
成员主要 是一个impl的指针。还有一个线程ID。
impl 类定义如下
class thread::impl final { public: std::thread t_; };
impl 类没有删减,只有一个 std::thread 的成员变量。让我们把注意力集中到thread 的run函数。这个比较重要,代码如下
1 bool thread::run(std::function<void()> && f) 2 { 3 if (impl_) { 4 return false; 5 } 6 7 try { 8 impl_ = new impl; 9 impl_->t_ = std::thread(std::move(f)); 10 } 11 catch (std::exception const&) { 12 delete impl_; 13 impl_ = nullptr; 14 } 15 16 return impl_ != nullptr; 17 }
thread类 run成员函数参数是一个模板类std::function。使用的模板类型是个函数。这个函数是void 返回,无参的一个函数。
为啥使用右引用。这是因为传入的函数可能是右值。另外左值也可以转右值。使用右引用比较方便。run()函数逻辑也很简单。就是new 一个impl,
然后用传入函数,构造一个线程类。赋值给 impl的成员。再来分析event_loop的构造函数。参照上述的构造函数。
thread_是唯一智能指针。用thread无参构造。然后指向thread 的run函数。run函数上面已经分析。现在看看传入参数。 [this] { entry(); }
这个函数是匿名函数。是 lambda表达式。不是很懂 lambda 表达式。匿名函数的执行体是就是thread类的entry函数。总的来说就是event_loop类构造时会构造内部成员,thread类智能指针。
然后调用thread 的run函数。run函数实际上是用匿名函数构造std::thread类。thread类在执行匿名函数时,会调用event_loop的 entry函数。
再来看event_loop的另外两个构造函数, 首先看用thread_pool 构造的。
event_loop::event_loop(thread_pool & pool) : sync_(false) { task_ = std::make_unique<async_task>(pool.spawn([this] { entry(); })); }
task_成员变量是 async_task 的一个智能指针。函数 pool.spawn 返回值一个 async_task的指针。看看spawn函数源码
1 async_task thread_pool::spawn(std::function<void()> const& f) 2 { 3 async_task ret; 4 5 if (f) { 6 scoped_lock l(m_); 7 8 pooled_thread_impl *t{}; 9 if (idle_.empty()) { 10 t = new pooled_thread_impl(*this); 11 if (!t->run()) { 12 delete t; 13 return ret; 14 } 15 threads_.push_back(t); 16 } 17 else { 18 t = idle_.back(); 19 idle_.pop_back(); 20 } 21 22 ret.impl_ = new async_task_impl; 23 ret.impl_->thread_ = t; 24 t->task_ = ret.impl_; 25 t->f_ = f; 26 t->thread_cond_.signal(l); 27 } 28 29 return ret; 30 }
spawn 函数参数是一个函数模板类,使用的模板也是一个函数类型。void 返回,无参的函数。 async_task 类成员impl_定义如下。
async_task_impl* impl_{};
async_task_impl 的定义如下。
1 1 class async_task_impl final 2 2 { 3 3 public: 4 4 pooled_thread_impl * thread_{}; 5 5 };
pooled_thread_impl类实现如下
1 class pooled_thread_impl final 2 { 3 public: 4 pooled_thread_impl(thread_pool & pool) 5 : m_(pool.m_) 6 , pool_(pool) 7 {} 8 9 virtual ~pooled_thread_impl() 10 { 11 thread_.join(); 12 } 13 14 bool run() 15 { 16 return thread_.run([this] { entry(); }); 17 } 18 19 virtual void entry() { 20 scoped_lock l(m_); 21 while (!quit_) { 22 thread_cond_.wait(l); 23 24 if (f_) { 25 l.unlock(); 26 f_(); 27 l.lock(); 28 task_ = nullptr; 29 f_ = std::function<void()>(); 30 pool_.idle_.push_back(this); 31 if (task_waiting_) { 32 task_waiting_ = false; 33 task_cond_.signal(l); 34 } 35 } 36 } 37 } 38 39 void quit(scoped_lock & l) 40 { 41 quit_ = true; 42 thread_cond_.signal(l); 43 } 44 45 thread thread_; 46 async_task_impl* task_{}; 47 std::function<void()> f_{}; 48 mutex & m_; 49 condition thread_cond_; 50 51 condition task_cond_; 52 53 thread_pool& pool_; 54 55 bool task_waiting_{}; 56 private: 57 bool quit_{}; 58 };
重要的成员函数一个是run(),一个是entry()函数。run函数实现上述已描述过。结合 thread_pool::spawn 函数。传入一个函数。先会去判断idle是不是空。idle_是一个容器。定义如下:
std::vector<pooled_thread_impl*> idle_;
如果是空,就是没有空闲的线程。就new 一个 pooled_thread_impl 类。然后执行成员函数run(), 这个成员run()执行的是thread类的成员函数run(),最终在某一个时机,thread线程会执行
pooled_thread_impl类的entry函数。pooled_thread_impl类的entry函数,是一个简单的循环,它在等一个信号。等到以后就会执行,函数f_,然后把f_,task_置空。然后把当前类指针存入到idle_中。就可以重复利用。
只需要设置新的回调函数和信号。执行的这个f_就是spawn 的传入参数。传入的实参是 [this] { entry(); } 是匿名函数,是lambda 表达式。总之有些复杂,反正知道,某一时机,会执行event_loop类entry()就行。
另外一个带参构造函数如下
1 event_loop::event_loop(event_loop::loop_option) 2 : sync_(false) 3 { 4 }
带参构造的参数没有用,作用可能是用来区别 另外一个带 thread_pool 参数的构造函数。如果用这个构造函数来构造 event_loop ,显然是没法用的
这就需要调用者,手动调用另外一个函数。event_loop 的run成员函数,代码如下
1 void event_loop::run() 2 { 3 if (task_ || thread_ || thread_id_ != thread::id()) { 4 return; 5 } 6 7 entry(); 8 }
非常简单的一个包装。前面是在判断一个成员变量是否有值。就是在判断是否采用了event_loop其他的两种构造方法。避免多次执行entry()方法。
看来这个entry方法十分重要。entry函数可以说是event_loop的核心处理过程。entry函数代码如下
1 void event_loop::entry() 2 { 3 thread_id_ = thread::own_id(); 4 5 monotonic_clock now; 6 7 scoped_lock l(sync_); 8 while (!quit_) { 9 if (process_timers(l, now)) { 10 continue; 11 } 12 if (process_event(l)) { 13 continue; 14 } 15 16 // Nothing to do, now we wait 17 if (deadline_) { 18 cond_.wait(l, deadline_ - now); 19 } 20 else { 21 cond_.wait(l); 22 } 23 } 24 }
event_loop 内部是一个循环,检测quit_成员变量有没有置。没有置的话·,开始处理。先处理timer类的事件。然后处理其他事件。最后就是等待信号。然后进入下次循环。
关键函数是 process_timers 和 process_event 函数。分别来看。
1 bool event_loop::process_timers(scoped_lock & l, monotonic_clock & now) 2 { 3 if (!deadline_) { 4 // There's no deadline 5 return false; 6 } 7 8 now = monotonic_clock::now(); 9 if (now < deadline_) { 10 // Deadline has not yet expired 11 return false; 12 } 13 14 // Update deadline_, stop at first expired timer 15 deadline_ = monotonic_clock(); 16 auto it = timers_.begin(); 17 for (; it != timers_.end(); ++it) { 18 if (!deadline_ || it->deadline_ < deadline_) { 19 if (it->deadline_ <= now) { 20 break; 21 } 22 deadline_ = it->deadline_; 23 } 24 } 25 26 if (it != timers_.end()) { 27 // 'it' is now expired 28 // deadline_ has been updated with prior timers 29 // go through remaining elements to update deadline_ 30 for (auto it2 = std::next(it); it2 != timers_.end(); ++it2) { 31 if (!deadline_ || it2->deadline_ < deadline_) { 32 deadline_ = it2->deadline_; 33 } 34 } 35 36 event_handler *const handler = it->handler_; 37 auto const id = it->id_; 38 39 // Update the expired timer 40 if (!it->interval_) { 41 timers_.erase(it); 42 } 43 else { 44 it->deadline_ = now + it->interval_; 45 if (!deadline_ || it->deadline_ < deadline_) { 46 deadline_ = it->deadline_; 47 } 48 } 49 50 // Call event handler 51 event_assert(!handler->removing_); 52 53 active_handler_ = handler; 54 55 l.unlock(); 56 (*handler)(timer_event(id)); 57 l.lock(); 58 59 active_handler_ = nullptr; 60 61 return true; 62 } 63 64 return false; 65 }
process_timers 函数处理用到了一个timers_成员变量。timers_定义如下。
1 struct FZ_PRIVATE_SYMBOL timer_data final 2 { 3 event_handler* handler_{}; 4 timer_id id_{}; 5 monotonic_clock deadline_; 6 duration interval_{}; 7 }; 8 9 typedef std::vector<timer_data> Timers;
10 Timers timers_;
process_timers 函数先判断deadline_有否有效, 无效的话,就没有timer事件类。如果现在的时间都小于deadline_,就说明还没有timer事件类到期。
接下来遍历timers,找出第一个过期的timer事件。然后更新 deadline_。就是距离现在最近的timer的截止事件。然后开始处理这个过期的timer事件类。
timer事件类分两种,一种就是一次性的。另一种就是执行多次的,有时间间隔的。对于前一种,需要在timers容器上移除掉。另外一种需要更新deadline_。
timer 过期时间和 局部变量 deadline_的值。然后调用注册的handler处理函数处理这个timer。处理结束。
然后看看event_loop::process_event函数。代码如下。
1 bool event_loop::process_event(scoped_lock & l) 2 { 3 Events::value_type ev{}; 4 5 if (pending_events_.empty()) { 6 return false; 7 } 8 ev = pending_events_.front(); 9 pending_events_.pop_front(); 10 11 event_assert(ev.first); 12 event_assert(ev.second); 13 event_assert(!ev.first->removing_); 14 15 active_handler_ = ev.first; 16 17 l.unlock(); 18 (*ev.first)(*ev.second); 19 delete ev.second; 20 l.lock(); 21 22 active_handler_ = nullptr; 23 24 return true; 25 }
process_event 成员变量 panding_events_ 定义如下
1 typedef std::deque<std::pair<event_handler*, event_base*>> Events; 2 Events pending_events_;
panding_events_ 是标准的std::deque队列。每个元素是一个键值对。第一个元素是 event_handler 事件处理器的指针。 第一个是event_base 事件类的指针。
process_event 函数从std::deque队列中取出一个事件然后调用事件处理器处理对应的事件。处理结束。
process_event 使用(*ev.first)(*ev.second); process_timers 使用(*handler)(timer_event(id)); 来处理事件。无论前者还是后者,括号内都是类。那这代码能执行吗。
答案是能执行。这使用了()重载函数或者说是仿函数。可以参考最上面的的例子。从filezilla找一些例子来看。首先看CFileZillaEnginePrivate 类中一个例子。代码如下。
1 void CFileZillaEnginePrivate::operator()(fz::event_base const& ev) 2 { 3 fz::scoped_lock lock(mutex_); 4 5 fz::dispatch<CFileZillaEngineEvent, CCommandEvent, CAsyncRequestReplyEvent, fz::timer_event, CInvalidateCurrentWorkingDirEvent, options_changed_event>(ev, this, 6 &CFileZillaEnginePrivate::OnEngineEvent, 7 &CFileZillaEnginePrivate::OnCommandEvent, 8 &CFileZillaEnginePrivate::OnSetAsyncRequestReplyEvent, 9 &CFileZillaEnginePrivate::OnTimer, 10 &CFileZillaEnginePrivate::OnInvalidateCurrentWorkingDir, 11 &CFileZillaEnginePrivate::OnOptionsChanged 12 ); 13 }
传入参数是fz::event_base 的一个实例。然后加了一把锁。调用fz::dispatch函数。估计很多人看到 fz::dispatch 都吓到了。这么多参数。
不要慌,冷静一下,分析分析,也不难。看看 fz::dispatch 的实现。代码在libfilezilla工程目录\lib\libfilezilla\event_handler.hpp。代码如下
1 template<typename T, typename ... Ts, typename H, typename F, typename ... Fs> 2 bool dispatch(event_base const& ev, H* h, F&& f, Fs&& ... fs) 3 { 4 if (dispatch<T>(ev, h, std::forward<F>(f))) { 5 return true; 6 } 7 8 return dispatch<Ts...>(ev, h, std::forward<Fs>(fs)...); 9 }
dispatch函数使用了5个模板参数第一个模板参数T 对应的调用者CFileZillaEngineEvent的类。第二个是变参模板就是可以有1个或多个模板类对用调用者 CCommandEvent 到options_changed_event。
ev 是调用dispath传入具体的一个event_base实例。H对应this 指针 F对应&CFileZillaEnginePrivate::OnEngineEvent 处理函数。Fs对应 &CFileZillaEnginePrivate::OnCommandEvent 到
&CFileZillaEnginePrivate::OnOptionsChanged 处理函数。先按照 CFileZillaEngineEvent 来类型,调用具体的处理函数处理,如果处理了,就返回。没处理,就再递归调用自己,直到找到一个能处理传入
event_base 的处理器,然后调用处理。
接下来看看dispatch函数如果判断类模板T,传入参数ev类二者是否匹配。是否该调用对应的对应函数。代码如下
1 template<typename T, typename H, typename F> 2 bool dispatch(event_base const& ev, H* h, F&& f) 3 { 4 bool const same = same_type<T>(ev); 5 if (same) { 6 T const* e = static_cast<T const*>(&ev); 7 apply(h, std::forward<F>(f), e->v_); 8 } 9 return same; 10 }
same_type 实现如下。
1 template<typename T> 2 bool same_type(event_base const& ev) 3 { 4 return ev.derived_type() == T::type(); 5 }
简单来说就是判断两个类型是否一致。一致的话调用相对应的处理函数。apply 具体实现就不贴了。
另外一个相关的dispatch函数在这里贴一下。
1 template<typename T, typename F> 2 bool dispatch(event_base const& ev, F&& f) 3 { 4 bool const same = same_type<T>(ev); 5 if (same) { 6 T const* e = static_cast<T const*>(&ev); 7 std::apply(std::forward<F>(f), e->v_); 8 } 9 return same; 10 }
这两个dispath 函数 使用的模板参数不同,原因在于,处理函数是否属于类。属于类的成员函数的应该使用三个模板参数的。否则应两个模板参数的。
上面介绍的其他事件处理类的仿函数。现在在看一个事件处理的仿函数。以CControlSocket 类中仿函数以为。
1 void CControlSocket::operator()(fz::event_base const& ev) 2 { 3 fz::dispatch<fz::timer_event, CObtainLockEvent>(ev, this, 4 &CControlSocket::OnTimer, 5 &CControlSocket::OnObtainLock); 6 }
可以看到和其他事件处理仿函数十分相似。就不再描述。
以上介绍的是event_loop是怎么处理timer类事件和其他事件流程。
现在结束怎么在event添加事件。因为 timer类事件和 其他事件类使用的容器不一样。处理机制也不太相同。所以二者添加方式也不同。
先看看时间类事件是怎么添加的。代码如下
1 timer_id event_loop::add_timer(event_handler* handler, duration const& interval, bool one_shot) 2 { 3 timer_data d; 4 d.handler_ = handler; 5 if (!one_shot) { 6 d.interval_ = interval; 7 } 8 d.deadline_ = monotonic_clock::now() + interval; 9 10 scoped_lock lock(sync_); 11 if (!handler->removing_) { 12 d.id_ = ++next_timer_id_; // 64bit, can this really ever overflow? 13 14 timers_.emplace_back(d); 15 if (!deadline_ || d.deadline_ < deadline_) { 16 // Our new time is the next timer to trigger 17 deadline_ = d.deadline_; 18 cond_.signal(lock); 19 } 20 } 21 return d.id_; 22 }
整体逻辑比较简单。传入参数 一个处理器的类。一个间隔类interval,还有一个参数one_shot,是不是执行一次。
如果是一次的话,就忽略传入的 interval。然后就是计算终止时间,置信号。然后返回一个timer_id。这个返回值有什么用呢。
用户可以保存起来。然后调用 event_loop的 stop_timer成员函数。取消对应的timer 事件处理。
event_loop的 stop_timer代码如下:
1 void event_loop::stop_timer(timer_id id) 2 { 3 if (id) { 4 scoped_lock lock(sync_); 5 for (auto it = timers_.begin(); it != timers_.end(); ++it) { 6 if (it->id_ == id) { 7 timers_.erase(it); 8 if (timers_.empty()) { 9 deadline_ = monotonic_clock(); 10 } 11 break; 12 } 13 } 14 } 15 }
传入参数是add_timer函数返回值。实现就是再timers对应中移除掉指定timer_id 的timer类事件。
接下来看看其他事件处理添加逻辑。代码如下
1 void event_loop::send_event(event_handler* handler, event_base* evt) 2 { 3 event_assert(handler); 4 event_assert(evt); 5 6 { 7 scoped_lock lock(sync_); 8 if (!handler->removing_) { 9 if (pending_events_.empty()) { 10 cond_.signal(lock); 11 } 12 pending_events_.emplace_back(handler, evt); 13 return; 14 } 15 } 16 17 delete evt; 18 }
可以看到 sent_event 传入参数一个event_handler 的指针。一个event_base 的实例。先去判断pending_events_队列是否是空,空的话就置信号。然后插入handler 和 evt 。
如果不是空的话,只有插入队列的操作。其他事件的添加使用实例可以参考本篇开头例子。
event_loop 中对于其他事件类有一个专门的处理函数。filter_events 函数。看函数名字就知道这就是一个其他事件过滤函数。代码实现如下
1 void event_loop::filter_events(std::function<bool(Events::value_type &)> const& filter) 2 { 3 scoped_lock l(sync_); 4 5 pending_events_.erase( 6 std::remove_if(pending_events_.begin(), pending_events_.end(), 7 [&](Events::value_type & v) { 8 bool const remove = filter(v); 9 if (remove) { 10 delete v.second; 11 } 12 return remove; 13 } 14 ), 15 pending_events_.end() 16 ); 17 }
filter_events 函数传入参数是一个 std::function模板类 使用的模板参数是一个函数。这个函数的返回值是bool 参数类型是 Events::value_type 引用类型。filter能代表传入的函数。
pending_events_.erase 内嵌套一个std::remove_if函数。从pending_event_第一个元素到最后一个元素。使用匿名函数。你们函数内部使用传入参数的回调来判断事件是不是需要移除。需要移除的,析构掉第二个元素类。
然后将这个元素移动到pending_events_末尾。经过迭代,pengding_events_末尾都是需要删除的元素。remove_if返回一个需要删除元素的前一个元素。然后使用erase方法删除这些元素。
下面这个代码是 CSftpControlSocket::DoClose调用的部分代码。
1 auto threadEventsFilter = [&](fz::event_loop::Events::value_type const& ev) -> bool { 2 if (ev.first != this) { 3 return false; 4 } 5 else if (ev.second->derived_type() == CSftpEvent::type() || ev.second->derived_type() == CTerminateEvent::type()) { 6 return true; 7 } 8 return false; 9 }; 10 11 event_loop_.filter_events(threadEventsFilter);
可以看到 上面这个过滤器过滤的是类型是 CSftpEvent::type类型 或者是 CTerminateEvent::type 类型的。
timer事件类用stop_timer移除指定的timer事件。其他事件类可以用filter_events 来过滤一些特别的事件。除此以外,event_loop还有一个更通用的函数可以用来移除一些特定的处理器。
这个函数就是remove_handler,代码如下:
1 void event_loop::remove_handler(event_handler* handler) 2 { 3 scoped_lock l(sync_); 4 5 handler->removing_ = true; 6 7 pending_events_.erase( 8 std::remove_if(pending_events_.begin(), pending_events_.end(), 9 [&](Events::value_type const& v) { 10 if (v.first == handler) { 11 delete v.second; 12 } 13 return v.first == handler; 14 } 15 ), 16 pending_events_.end() 17 ); 18 19 timers_.erase( 20 std::remove_if(timers_.begin(), timers_.end(), 21 [&](timer_data const& v) { 22 return v.handler_ == handler; 23 } 24 ), 25 timers_.end() 26 ); 27 if (timers_.empty()) { 28 deadline_ = monotonic_clock(); 29 } 30 31 if (active_handler_ == handler) { 32 if (thread::own_id() != thread_id_) { 33 while (active_handler_ == handler) { 34 l.unlock(); 35 yield(); 36 l.lock(); 37 } 38 } 39 } 40 }
实现就是在两个容易中遍历,删除符合条件的handler的事件。
event_loop类除了以上的函数以外,还有一个 stop函数。代码如下
1 void event_loop::stop(bool join) 2 { 3 { 4 scoped_lock l(sync_); 5 quit_ = true; 6 cond_.signal(l); 7 } 8 9 if (join) { 10 thread_.reset(); 11 task_.reset(); 12 13 scoped_lock lock(sync_); 14 for (auto & v : pending_events_) { 15 delete v.second; 16 } 17 pending_events_.clear(); 18 19 timers_.clear(); 20 deadline_ = monotonic_clock(); 21 } 22 }
stop函数设置退出标准,重置了信号。随便就遍历pending_events_,析构第二个元素。删除元素。清空timers_容器。
到此位置,event_loop类基本已经介绍完成了。
简单回顾一下,做个总结。
eventloop有三种构造方式,无参构造函数,带thread_pool 类的构造,以及 带event_loop::loop_option 参数构造。前面两种构造时会调用event_loop的entry函数。
entry函数线程的执行体,处理timer事件类和 其他的事件类,与时间无关的事件。使用后者构造,需要手动调用run()函数。run()中会调用entry函数。
entry函数执行以后,就可以添加事件了。timer类的事件用add_timer类函数其他事件用send_event函数。timer类事件移除调用stop_timer函数。其他事件类调用event_filter来移除一些事件。
二者都可以用remove_handler来 移除的特定的handler 处理的事件。想要结束处理的话,就调用stop函数。清理线程,清理事件。
整体上就是这样,希望有所收获。
参考:https://www.cplusplus.com/reference/algorithm/remove_if/
https://blog.csdn.net/kfling/article/details/80187847
浙公网安备 33010602011771号