Future
非同期メッセージの戻り値を受け取るためのパターン
これ、C# の同期メソッドの非同期呼び出しで使われてるパターンと同じなのかな。
data クラスは IAsyncResult に対応して、host クラスは Begin〜, End〜 を持ってるクラスに対応してる感じ。
以下は増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編のサンプルを Boost.Thread を使って書いたコード。
main.cpp
#include <boost/shared_ptr.hpp> #include "data.h" #include "host.h" #include "../thread_helper.h" namespace mtdp{ namespace future { void main() { thread_helper::shared_cout("main BEGIN\n"); boost::shared_ptr<host> h(new host()); boost::shared_ptr<data> data1 = h->request(10, 'A'); boost::shared_ptr<data> data2 = h->request(20, 'B'); boost::shared_ptr<data> data3 = h->request(30, 'C'); thread_helper::shared_cout("main otherJob BEGIN\n"); thread_helper::sleep(2000); thread_helper::shared_cout("main otherJob END\n"); thread_helper::shared_cout("data1 = " + data1->content() + "\n"); thread_helper::shared_cout("data2 = " + data2->content() + "\n"); thread_helper::shared_cout("data3 = " + data3->content() + "\n"); thread_helper::shared_cout("main END\n"); } }}
host.h
#ifndef MTDP_FUTURE_HOST_H_INCLUDED #define MTDP_FUTURE_HOST_H_INCLUDED #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/bind.hpp> #include "data.h" #include "real_data.h" #include "future_data.h" #include "../thread_helper.h" namespace mtdp{ namespace future { class host { private: boost::shared_ptr<boost::thread> thread_; public: ~host() { if (thread_) { try { thread_->join(); } catch (...) { } } } boost::shared_ptr<data> request(std::size_t count, char c) { thread_helper::shared_cout(" request(" + to_string(count) + ", " + to_string(c) + ") BEGIN\n"); // (1) future_data のインスタンスを作る boost::shared_ptr<future_data> future(new future_data()); // (2) read_data のインスタンスを作るための新しいスレッドを起動する thread_.reset(new boost::thread(boost::bind(&host::run, future, count, c))); thread_helper::shared_cout(" request(" + to_string(count) + ", " + to_string(c) + ") END\n"); // (3) future_data のインスタンスを戻り値とする return future; } private: static void run(boost::shared_ptr<future_data> future, std::size_t count, char c) { boost::shared_ptr<real_data> rd(new real_data(count, c)); future->set_real_data(rd); } }; }} #endif // MTDP_FUTURE_HOST_H_INCLUDED
data.h
#ifndef MTDP_FUTURE_DATA_H_INCLUDED #define MTDP_FUTURE_DATA_H_INCLUDED #include <string> namespace mtdp{ namespace future { class data { public: virtual std::string content() = 0; }; }} #endif // MTDP_FUTURE_DATA_H_INCLUDED
future_data.h
#ifndef MTDP_FUTURE_FUTURE_DATA_H_INCLUDED #define MTDP_FUTURE_FUTURE_DATA_H_INCLUDED #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <string> #include "data.h" #include "real_data.h" namespace mtdp{ namespace future { class future_data : public data { private: boost::shared_ptr<real_data> real_data_; volatile bool ready_; boost::mutex mutex_; boost::condition condition_; public: future_data() : ready_(false) { } void set_real_data(boost::shared_ptr<real_data> rd) { boost::mutex::scoped_lock lock(mutex_); if (ready_) { return; // balk } real_data_ = rd; ready_ = true; condition_.notify_all(); } virtual std::string content() { boost::mutex::scoped_lock lock(mutex_); while (!ready_) { condition_.wait(lock); } return real_data_->content(); } }; }} #endif // MTDP_FUTURE_FUTURE_DATA_H_INCLUDED
real_data.h
#ifndef MTDP_FUTURE_REAL_DATA_H_INCLUDED #define MTDP_FUTURE_REAL_DATA_H_INCLUDED #include <boost/foreach.hpp> #include <string> #include <vector> #include "data.h" #include"../thread_helper.h" namespace mtdp{ namespace future { class real_data : public data { private: const std::string content_; public: real_data(std::size_t count, char c) : content_(construct_content(count, c)) { } virtual std::string content() { return content_; } private: std::string construct_content(std::size_t count, char c) { thread_helper::shared_cout(" making real_data(" + to_string(count) + ", " + to_string(c) + ") BEGIN\n"); std::vector<char> buffer; buffer.reserve(count); for (std::size_t i = 0; i < count; i++) { buffer.push_back(c); thread_helper::sleep(100); } thread_helper::shared_cout(" making real_data(" + to_string(count) + ", " + to_string(c) + ") END\n"); return std::string(buffer.begin(), buffer.end()); } }; }} #endif // MTDP_FUTURE_REAL_DATA_H_INCLUDED
実行結果
main BEGIN request(10, A) BEGIN making real_data(10, A) BEGIN request(10, A) END request(20, B) BEGIN making real_data(20, B) BEGIN request(20, B) END request(30, C) BEGIN making real_data(30, C) BEGIN request(30, C) END main otherJob BEGIN making real_data(10, A) END main otherJob END data1 = AAAAAAAAAA making real_data(20, B) END data2 = BBBBBBBBBBBBBBBBBBBB making real_data(30, C) END data3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC main END