Thread-Specific Storage


スレッド固有の領域を扱うパターン。
Boost.Thread には boost::thread_specific_ptr というクラスがあるので、これを使えば実現できる。


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


ts_log.h

#ifndef MTDP_THREAD_SPECIFIC_STORAGE_TS_LOG_H_INCLUDED
#define MTDP_THREAD_SPECIFIC_STORAGE_TS_LOG_H_INCLUDED

#include <fstream>
#include <string>

namespace mtdp{ namespace thread_specific_storage
{
    class ts_log
    {
    private:
        std::fstream fs_;

    public:
        ts_log(const std::string& filename)
            : fs_(filename.c_str(), std::ios_base::out)
        {
        }

        // ログを書く
        void println(const std::string& s)
        {
            fs_ << s << std::endl;
        }

        // ログを閉じる
        void close()
        {
            fs_ << "==== End of log ====" << std::endl;
            fs_.close();
        }
    };
}}

#endif // MTDP_THREAD_SPECIFIC_STORAGE_TS_LOG_H_INCLUDED

log.h

#ifndef MTDP_THREAD_SPECIFIC_STORAGE_LOG_H_INCLUDED
#define MTDP_THREAD_SPECIFIC_STORAGE_LOG_H_INCLUDED

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

namespace mtdp{ namespace thread_specific_storage
{
    class simple_log
    {
    private:
        struct instance
        {
            std::fstream fs_;
            instance() : fs_("log.txt", std::ios_base::out) { }
            ~instance() { fs_.close(); }
        };

        static instance& get()
        {
            static instance inst;
            return inst;
        }

    public:
        // ログを書く
        static void println(std::string s)
        {
            get().fs_ << s << std::endl;
        }

        // ログを閉じる
        static void close()
        {
            get().fs_ << "==== End of log ====" << std::endl;
            get().fs_.close();
        }
    };

    class log
    {
    public:
        // ログを書く
        template<class T>
        static void println(T* p, std::string s)
        {
            get_ts_log(p)->println(s);
        }

        // ログを閉じる
        template<class T>
        static void close(T* p)
        {
            get_ts_log(p)->close();
        }

    private:
        // スレッド固有のログを得る
        template<class T>
        static ts_log* get_ts_log(T* p)
        {
            static boost::thread_specific_ptr<ts_log> tss_log;

            ts_log* tl = tss_log.get();
            if (tl == 0)
            {
                tl = new ts_log(p->name() + "-log.txt");
                tss_log.reset(tl);
            }
            return tl;
        }
    };
}}

#endif // MTDP_THREAD_SPECIFIC_STORAGE_LOG_H_INCLUDED

client_thread.h

#ifndef MTDP_THREAD_SPECIFIC_STRAGE_CLIENT_THREAD_H_INCLUDED
#define MTDP_THREAD_SPECIFIC_STRAGE_CLIENT_THREAD_H_INCLUDED

#include <string>
#include "log.h"
#include "../thread_helper.h"

namespace mtdp{ namespace thread_specific_storage
{
    class client_thread
    {
    private:
        std::string name_;

    public:
        client_thread(std::string name)
            : name_(name)
        {
        }

        void run()
        {
            thread_helper::shared_cout(name() + " BEGIN\n");
            for (int i = 0; i < 10; i++)
            {
                log::println(this, "i = " + to_string(i));
                thread_helper::sleep(100);
            }
            log::close(this);
            thread_helper::shared_cout(name() + " BEGIN\n");
        }

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

#endif // MTDP_THREAD_SPECIFIC_STRAGE_CLIENT_THREAD_H_INCLUDED

main.cpp

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include "log.h"
#include "client_thread.h"
#include "../thread_helper.h"

namespace mtdp{ namespace thread_specific_storage
{
    void main_single()
    {
        std::cout << "BEGIN" << std::endl;
        for (int i = 0; i < 10; i++)
        {
            simple_log::println("main: i = " + to_string(i));
            thread_helper::sleep(100);
        }
        simple_log::close();
        std::cout << "END" << std::endl;
    }

    void main_multi()
    {
        boost::thread_group group;
        group.create_thread(boost::bind(&client_thread::run, boost::shared_ptr<client_thread>(new client_thread("Alice"))));
        group.create_thread(boost::bind(&client_thread::run, boost::shared_ptr<client_thread>(new client_thread("Bobby"))));
        group.create_thread(boost::bind(&client_thread::run, boost::shared_ptr<client_thread>(new client_thread("Chris"))));
        group.join_all();
    }

    void main()
    {
        // main_single();
        // ↑シングルスレッド用のサンプルはこっち

        main_multi();
        // ↑マルチスレッド用のサンプルはこっち
    }
}}