Guarded Suspension


適切な状態になっていないならスレッドを待たせる、というパターン。
boost::condition を使って実現することが出来る。


以下は増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編のサンプルを Boost.Thread を使って書いたコード。


request.h

#ifndef MTDP_GUARDED_SUSPENSION_REQUEST_H_INCLUDED
#define MTDP_GUARDED_SUSPENSION_REQUEST_H_INCLUDED

#include <string>

namespace mtdp{ namespace guarded_suspension
{
    class request
    {
    private:
        const std::string name_;

    public:
        request(std::string name)
            : name_(name)
        {
        }
        std::string name()
        {
            return name_;
        }
        std::string to_string()
        {
            return "[ Request " + name_ + " ]";
        }
    };
}}

#endif // MTDP_GUARDED_SUSPENSION_REQUEST_H_INCLUDED

request_queue.h

#ifndef MTDP_GUARDED_SUSPENSION_REQUEST_QUEUE_H_INCLUDED
#define MTDP_GUARDED_SUSPENSION_REQUEST_QUEUE_H_INCLUDED

#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <queue>
#include "request.h"

namespace mtdp{ namespace guarded_suspension
{
    class request_queue
    {
    private:
        std::queue<boost::shared_ptr<request> > queue_;
        boost::mutex mutex_;
        boost::condition condition_;

    public:
        boost::shared_ptr<request> get_request()
        {
            boost::mutex::scoped_lock lock(mutex_);

            while (queue_.empty())
            {
                condition_.wait(lock);
            }
            // ↓こんな風に書くことも出来る
            // condition_.wait(lock, !boost::bind(&std::queue<boost::shared_ptr<request> >::empty, &queue_));

            boost::shared_ptr<request> r = queue_.front();
            queue_.pop();
            return r;
        }
        void put_request(boost::shared_ptr<request> r)
        {
            boost::mutex::scoped_lock lock(mutex_);

            queue_.push(r);
            condition_.notify_all();
        }
    };
}}

#endif // MTDP_GUARDED_SUSPENSION_REQUEST_QUEUE_H_INCLUDED

client_thread.h

#ifndef MTDP_GUARDED_SUSPENSION_CLIENT_THREAD_H_INCLUDED
#define MTDP_GUARDED_SUSPENSION_CLIENT_THREAD_H_INCLUDED

#include <boost/random.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <string>
#include "request_queue.h"
#include "../thread_helper.h"

namespace mtdp{ namespace guarded_suspension
{
    class client_thread
    {
    private:
        const std::string name_;
        const boost::shared_ptr<request_queue> request_queue_;
        const boost::mt19937 mt_;

    public:
        client_thread(boost::shared_ptr<request_queue> rq, std::string name, boost::uint32_t seed)
            : name_(name), request_queue_(rq), mt_(seed)
        {
        }

        void run()
        {
            boost::variate_generator<boost::mt19937, boost::uniform_int<> > random(mt_, boost::uniform_int<>(0, 1000));
            for (int i = 0; i < 10000; i++)
            {
                boost::shared_ptr<request> r = request_queue_->get_request();
                thread_helper::shared_cout(name_ + " handles " + r->to_string() + "\n");
                thread_helper::sleep(random());
            }
        }
    };
}}

#endif // MTDP_GUARDED_SUSPENSION_CLIENT_THREAD_H_INCLUDED

server_thread.h

#ifndef MTDP_GUARDED_SUSPENSION_SERVER_THREAD_H_INCLUDED
#define MTDP_GUARDED_SUSPENSION_SERVER_THREAD_H_INCLUDED

#include <boost/random.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <string>
#include "request_queue.h"
#include "../thread_helper.h"

namespace mtdp{ namespace guarded_suspension
{
    class server_thread
    {
    private:
        const std::string name_;
        const boost::shared_ptr<request_queue> request_queue_;
        const boost::mt19937 mt_;

    public:
        server_thread(boost::shared_ptr<request_queue> rq, std::string name, boost::uint32_t seed)
            : name_(name), request_queue_(rq), mt_(seed)
        {
        }

        void run()
        {
            boost::variate_generator<boost::mt19937, boost::uniform_int<> > random(mt_, boost::uniform_int<>(0, 1000));
            for (int i = 0; i < 10000; i++)
            {
                boost::shared_ptr<request> r(new request("No." + to_string(i)));
                thread_helper::shared_cout(name_ + " requests " + r->to_string() + "\n");
                request_queue_->put_request(r);
                thread_helper::sleep(random());
            }
        }
    };
}}

#endif // MTDP_GUARDED_SUSPENSION_SERVER_THREAD_H_INCLUDED

main.cpp

#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include "request_queue.h"
#include "client_thread.h"
#include "server_thread.h"

namespace mtdp{ namespace guarded_suspension
{
    void main()
    {
        boost::shared_ptr<request_queue> rq(new request_queue());

        boost::thread_group group;
        group.create_thread(boost::bind(&client_thread::run, boost::shared_ptr<client_thread>(new client_thread(rq, "Alice", 3141592))));
        group.create_thread(boost::bind(&server_thread::run, boost::shared_ptr<server_thread>(new server_thread(rq, "Bobby", 6535897))));
        group.join_all();
    }
}}