Boost.Asio はもうちょっと↓みたいに(ry
#define WIN32_LEAN_AND_MEAN #include <cstdio> #include <algorithm> #include <iostream> #include <boost/bind.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/deadline_timer.hpp> class Handler { public: explicit Handler(boost::asio::io_service &proactor) : proactor_(proactor), i_(1), p_timer_(new boost::asio::deadline_timer(proactor_)) { p_timer_->expires_from_now(boost::posix_time::seconds(1)); p_timer_->async_wait(boost::bind(&Handler::onTime, this)); } void initiate() { proactor_.post(boost::bind(&Handler::initiate, this)); proactor_.post(boost::bind(&Handler::handle, this, i_, i_, 0, i_)); ++i_; } void handle(int i, int src, int count, int max) { if (i == 1) { std::cout << "from: " << src << ", count: " << count << ", max: " << max << std::endl; return; } int new_i = i; new_i = new_i % 2 == 0 ? new_i / 2 : new_i * 3 + 1; std::cout << i << " -> " << new_i << " (started from " << src << ")" << std::endl; proactor_.post(boost::bind(&Handler::handle, this, new_i, src, count + 1, new_i > max ? new_i : max)); } void onTime() { std::cout << "(^o^)(^o^)(^o^)(^o^)(^o^)(^o^)(^o^)(^o^)(^o^)(^o^)" << std::endl; p_timer_->expires_from_now(boost::posix_time::seconds(1)); p_timer_->async_wait(boost::bind(&Handler::onTime, this)); } private: boost::asio::io_service &proactor_; int i_; boost::shared_ptr<boost::asio::deadline_timer> p_timer_; }; // class Handler void threadMain(boost::asio::io_service &proactor) { Handler h(proactor); proactor.post(boost::bind(&Handler::initiate, boost::ref(h))); proactor.run(); } int main() { boost::asio::io_service proactor; boost::thread th(boost::bind(&threadMain, boost::ref(proactor))); std::fgetc(stdin); proactor.stop(); th.join(); }
先のコードがやっていることだけだと, proactor_.post ではなくて普通の関数呼び出しでよくて (ただし,先のコードの proactor_.post を普通の関数呼び出しに単純に書き換えると stack overflow なコードになるんですが) , proactor にコントロールを戻す意味が「面白そうだからやってみた」以外にはほぼないといってよいです.そこで,先のコードがやっていることに非同期なイベントを加えたのが上です.先のような通常の関数呼び出しに類似した制御フローと,非同期処理を単一スレッド上で実現しています.
単一スレッドで handler を駆動しているため,当然ながら handler だけが触るオブジェクト (上では std::cout) はロックがまったく不要です.
余談になりますが,若干の変更で proactor / handler を複数スレッドでも駆動させられるようにすることができます.このようなマルチスレッド環境下での駆動においても, proactor pattern には普通のマルチスレッドプログラミングでは必要となるようなロックをできるだけ回避するような工夫が考えられますけれどこれ誰か整理してくれにゃいかにゃー.もちろん,非同期 I/O との組み合わせによって I/O のスケジューリング等を OS に暗黙かつ効率的に依存できるなどの点は指摘するまでもないんですが.