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