C++標準ライブラリの future/promise を利用してone-shotイベントを実現する。
本記事での “one-shotイベント” は、1度しか使えないスレッド間イベント待機/通知機構を指している。環境依存の実装であれば、WaitForSingleObject/SetEvent(Windows API)やsem_wait/sem_post(POSIX)の組で実現可能*1。
std::promise<void>(イベント通知側)とstd::shared_future<void>(複数のイベント待機側)を利用したone-shotイベントの実装コード。
#include <thread> #include <future> void func(std::shared_future<void> ftr) { //... ftr.wait(); // イベント待機 //... } int main() { std::promise<void> prm; auto ftr = prm.get_future().share(); std::thread t1(func, ftr), t2(func, ftr); //... prm.set_value(); // イベント通知 //... t1.join(); t2.join() }
shared_future::waitでイベント待機中の2つのスレッドt1, t2に対し、mainスレッドからpromise::set_valueでイベントシグナル通知を行っている。また、イベント待機をshared_future::getで行い、通知側はpromise::set_value(正常時)とset_exception(異常時)で使い分ければ待機側スレッドへの例外通知を実現できる。
おまけ:mutex+condition_variable版
mutexとcondition_variableを利用したイベントオブジェクトの単純な実装コード。こちらはイベントリセット(resetメンバ関数)を提供するため、イベントオブジェクトの使い回しが可能となる。
#include <mutex> #include <condition_variable> class event { private: std::mutex m_; std::condition_variable cv_; bool flag_; public: explicit event(bool init_flag = false) : flag_(init_flag) {} event(const event&) = delete; event& operator=(const event&) = delete; void wait() { std::unique_lock<std::mutex> lk(m_); cv_.wait(lk, [&]{ return flag_; }); } void set() { std::lock_guard<std::mutex> lk(m_); flag_ = true; cv_.notify_all(); } void reset() { std::lock_guard<std::mutex> lk(m_); flag_ = false; } };