Balking


適切な状態になっていないなら処理を中断する、というパターン。


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


save, doSave メソッドは Thread#currentThread を必要としていたんだけど、boost::thread にはそんなのは無いので this を渡して無理矢理名前を取得しています。
最初は TSS を使ってカレントスレッドを取得していたんだけど、何か冗長になってきたので上記の方法に変更してみました。


data.h

#ifndef MTDP_BALKING_DATA_H_INCLUDED
#define MTDP_BALKING_DATA_H_INCLUDED

#include <boost/thread.hpp>
#include <string>
#include <iostream>
#include <fstream>
#include "../thread_helper.h"

namespace mtdp{ namespace balking
{
    class data
    {
    private:
        const std::string filename_;    // 保存するファイルの名前
        std::string content_;           // データの内容
        bool changed_;                  // 変更した内容が保存されていないなら true
        boost::mutex mutex_;

    public:
        data(std::string filename, std::string content)
            : filename_(filename), content_(content), changed_(true)
        {
        }

        // データの内容を書き換える
        void change(std::string newContent)
        {
            boost::mutex::scoped_lock lock(mutex_);

            content_ = newContent;
            changed_ = true;
        }

        // データの内容が変更されていたらファイルに保存する
        template<class TThread>
        void save(TThread* p)
        {
            boost::mutex::scoped_lock lock(mutex_);

            if (!changed_)
            {
                return;
            }
            doSave(p);
            changed_ = false;
        }

    private:
        // データの内容を実際にファイルに保存する
        template<class TThread>
        void doSave(TThread* p)
        {
            thread_helper::shared_cout(p->name() + " calls doSave, content = " + content_ + "\n");
            std::fstream fs(filename_.c_str(), std::ios_base::out);
            fs << (content_ + "\n");
        }
    };
}}

#endif // MTDP_BALKING_DATA_H_INCLUDED

saver_thread.h

#ifndef MTDP_BALKING_SAVER_THREAD_H_INCLUDED
#define MTDP_BALKING_SAVER_THREAD_H_INCLUDED

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <string.h>
#include "data.h"
#include "../thread_helper.h"

namespace mtdp{ namespace balking
{
    class saver_thread
    {
    private:
        const std::string name_;
        const boost::shared_ptr<data> data_;

    public:
        saver_thread(const std::string& name, boost::shared_ptr<data> d)
            : name_(name), data_(d)
        {
        }

        void run()
        {
            while (true)
            {
                data_->save(this);
                thread_helper::sleep(1000);
            }
        }

        std::string name() const
        {
            return name_;
        }
    };
}}

#endif // MTDP_BALKING_SAVER_THREAD_H_INCLUDED

changer_thread.h

#ifndef MTDP_BALKING_CHANGER_THREAD_H_INCLUDED
#define MTDP_BALKING_CHANGER_THREAD_H_INCLUDED

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

namespace mtdp{ namespace balking
{
    class changer_thread
    {
    private:
        const std::string name_;
        const boost::shared_ptr<data> data_;
        const boost::mt19937 mt_;

    public:
        changer_thread(std::string name, boost::shared_ptr<data> d)
            : name_(name), data_(d)
        {
        }

        void run()
        {
            boost::variate_generator<boost::mt19937, boost::uniform_int<> > random(mt_, boost::uniform_int<>(0, 1000));
            for (int i = 0; true; i++)
            {
                data_->change("No." + to_string(i));    // データを変更する
                thread_helper::sleep(random());         // 仕事のつもり
                data_->save(this);                      // 明示的に保存する
            }
        }

        std::string name() const
        {
            return name_;
        }
    };
}}

#endif // MTDP_BALKING_CHANGER_THREAD_H_INCLUDED

main.cpp

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include "data.h"
#include "saver_thread.h"
#include "changer_thread.h"

namespace mtdp{ namespace balking
{
    void main()
    {
        boost::shared_ptr<data> d(new data("data.txt", "(empty)"));

        boost::thread_group group;
        group.create_thread(boost::bind(&changer_thread::run, boost::shared_ptr<changer_thread>(new changer_thread("ChangerThread", d))));
        group.create_thread(boost::bind(&saver_thread::run, boost::shared_ptr<saver_thread>(new saver_thread("SaverThread", d))));
        group.join_all();
    }
}}