async_delegate
汎用的に非同期メッセージを投げて戻り値を返してくれるクラスを作りました。
async_delegate::invoke に関数を設定すると、その関数を別スレッドから呼び出します。
async_delegate::invoke の戻り値である async_result_ptr を使って、同期処理を行い、呼び出した関数の戻り値を取得します。
現在は void を返すメソッドには対応していません。
Future パターンを汎用的にした感じですね。
一応 Worker Thread も設定できるようにしています。そっちはテストしてないですが。
使い方としてはこんな感じ
#include <boost/lexical_cast.hpp> #include <iostream> #include <string> #include "async_delegate.h" // for ::Sleep #include <windows.h> class test { private: async_delegate<int> async_delegate_; async_result_ptr<int> result1_; async_result_ptr<int> result2_; int calc_func(int count) { std::cout << "BEGIN calc_func: " + boost::lexical_cast<std::string>(count) + "\n"; int n = 0; for (int i = 0; i < count; i++) { n += 10; ::Sleep(100); } std::cout << "END calc_func: " + boost::lexical_cast<std::string>(count) + "\n"; return n; } public: void pre_calc() { result1_ = async_delegate_.invoke(boost::bind(&test::calc_func, this, 10)); result2_ = async_delegate_.invoke(boost::bind(&test::calc_func, this, 20)); } void display() { std::cout << "display result1_\n"; std::cout << result1_->get_result() << std::endl; std::cout << "display result2_\n"; std::cout << result2_->get_result() << std::endl; } }; void main() { test t; // 先に計算させておく t.pre_calc(); // こっちでいろいろ処理を行う ::Sleep(1500); // 本当に必要になったので表示してもらう。 t.display(); }
実行結果
BEGIN calc_func: 10 BEGIN calc_func: 20 END calc_func: 10 display result1_ 100 display result2_ // この段階でまだ calc_func(20) は完了していない END calc_func: 20 // ここで完了する 200 // 無事に結果を表示することが出来た
以下ソース
async_delegate.h
#ifndef ASYNC_DELEGATE_H_INCLUDED #define ASYNC_DELEGATE_H_INCLUDED #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> #include "async_result.h" class no_use_thread_pool { private: boost::thread_group group_; public: ~no_use_thread_pool() { group_.join_all(); } void invoke(boost::function0<void> function) { group_.create_thread(function); } }; template<class TResult, class TThreadPool = no_use_thread_pool> class async_delegate { private: boost::shared_ptr<TThreadPool> thread_pool_; public: async_delegate() { } async_delegate(boost::shared_ptr<TThreadPool> pool) : thread_pool_(pool) { } async_result_ptr<TResult> invoke(boost::function0<TResult> function) { async_result_ptr<TResult> ar(new async_result<TResult>()); if (!thread_pool_) { thread_pool_.reset(new TThreadPool()); } thread_pool_->invoke(boost::bind(&async_delegate<TResult>::_invoke, ar, function)); return ar; } private: static void _invoke(async_result_ptr<TResult> ar, boost::function0<TResult> function) { detail::async_result_setter<TResult>()(ar, function()); } }; #endif // ASYNC_DELEGATE_H_INCLUDED
async_result.h
#ifndef ASYNC_RESULT_H_INCLUDED #define ASYNC_RESULT_H_INCLUDED #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/optional.hpp> namespace detail { template<class TResult> struct async_result_setter; } template<class TResult> class async_result { private: boost::optional<TResult> result_; boost::mutex mutex_; boost::condition condition_; public: bool is_completed() { boost::mutex::scoped_lock lock(mutex_); return result_.is_initialized(); } TResult get_result() { boost::mutex::scoped_lock lock(mutex_); while (!result_.is_initialized()) { condition_.wait(lock); } return boost::get<TResult>(result_); } private: void set_result(TResult result) { boost::mutex::scoped_lock lock(mutex_); result_ = result; condition_.notify_all(); } friend struct detail::async_result_setter<TResult>; }; // template<class TResult> // typedef boost::shared_ptr<async_result<TResult> > async_result_ptr<TResult>; // ↑こうやって書ければいいのに、この機能がないので↓みたいな書き方になる // shared_ptr の typedef template<class T> class async_result_ptr : public boost::shared_ptr<async_result<T> > { private: typedef boost::shared_ptr<async_result<T> > base; public: async_result_ptr() : base() { } template<class Y> explicit async_result_ptr(Y* p) : base(p) { } template<class Y, class D> async_result_ptr(Y* p, D d) :base(p, d) { } template<class Y, class D, class A> async_result_ptr(Y* p, D d, A a) : base(p, d, a) { } #if defined(__BORLANDC__) || defined(__GNUC__) boost::shared_ptr<T>& operator=(boost::shared_ptr<T> const & r) // never throws { return base::operator=(r); } #endif template<class Y> explicit async_result_ptr(boost::weak_ptr<Y> const & r) : base(r) { } template<class Y> async_result_ptr(boost::shared_ptr<Y> const & r) : base(r) { } template<class Y> async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::static_cast_tag t) : base(r, t) { } template<class Y> async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::const_cast_tag t) : base(r, t) { } template<class Y> async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::dynamic_cast_tag t) : base(r, t) { } template<class Y> async_result_ptr(boost::shared_ptr<Y> const & r, boost::detail::polymorphic_cast_tag t) : base(r, t) { } #ifndef BOOST_NO_AUTO_PTR template<class Y> explicit async_result_ptr(std::auto_ptr<Y> & r) :base(r) { } #if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template<class Ap> explicit async_result_ptr(Ap r, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type t = 0) : base(r, t) { } #endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION #endif // BOOST_NO_AUTO_PTR #if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1300) template<class Y> boost::shared_ptr<T>& operator=(boost::shared_ptr<Y> const & r) // never throws { return base::operator=(r); } #endif #ifndef BOOST_NO_AUTO_PTR template<class Y> boost::shared_ptr<T>& operator=(std::auto_ptr<Y> & r) { return base::operator=(r); } #if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template<class Ap> typename boost::detail::sp_enable_if_auto_ptr<Ap, shared_ptr<T>&>::type operator=(Ap r) { return base::operator=(r); } #endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION #endif // BOOST_NO_AUTO_PTR }; namespace detail { template<class TResult> struct async_result_setter { void operator()(async_result_ptr<TResult> ar, TResult result) { ar->set_result(result); } }; } #endif // ASYNC_RESULT_H_INCLUDED