既存のオブジェクトをスレッドセーフにする

こんなの作ってみました。

#ifndef ANMELT_THREAD_MUTEXED_H_INCLUDED
#define ANMELT_THREAD_MUTEXED_H_INCLUDED

#include <boost/thread.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/type_traits/is_void.hpp>

namespace anmelt{ namespace thread
{

template<class T, class Mutex = boost::mutex>
class mutexed
{
private:
    T& value_;
    mutable Mutex mutex_;

public:
    mutexed(T& value) : value_(value) { }

    template<class F>
    typename boost::result_of<F(T&)>::type apply(F f)
    {
        typename Mutex::scoped_lock lock(mutex_);
        return f(value_);
    }

    template<class F>
    typename boost::result_of<F(const T&)>::type apply(F f) const
    {
        typename Mutex::scoped_lock lock(mutex_);
        return f(value_);
    }
};

}}

#endif // ANMELT_THREAD_MUTEXED_H_INCLUDED

mutexed の初期化時にマルチスレッド対応にしたいオブジェクトを指定して、あとは mutexed 経由でメソッドを指定するだけ。


以下テスト

#include <anmelt/thread/thread_pool.h>
#include <anmelt/thread/mutexed.h>
#include <boost/lambda/lambda.hpp>
#include <boost/foreach.hpp>

using namespace boost::posix_time;

void func1(anmelt::thread::mutexed<std::vector<int> >& vec)
{
    for (int i = 0; i < 30; i++)
    {
        // vec.push_back(i) をスレッドセーフで実行
        vec.apply(bind(&std::vector<int>::push_back, _1, i));
        boost::thread::sleep(boost::get_system_time() + milliseconds(1));
    }
}

void func2(anmelt::thread::mutexed<std::vector<int> >& vec)
{
    for (int i = 0; i < 20; i++)
    {
        struct pred : std::unary_function<std::vector<int>, void>
        {
            void operator()(std::vector<int>& v) const
            {
                std::for_each(v.begin(), v.end(), boost::lambda::_1++);
            }
        };
        // std::for_each(vec.begin(), vec.end(), boost::lambda::_1++) をスレッドセーフで実行
        vec.apply(pred());
        boost::thread::sleep(boost::get_system_time() + milliseconds(1));
    }
}

void main()
{
    std::vector<int> vec;
    anmelt::thread::mutexed<std::vector<int> > mutexed_vec(vec);

    boost::thread_group group;
    group.create_thread(bind(func1, boost::ref(mutexed_vec)));
    group.create_thread(bind(func2, boost::ref(mutexed_vec)));
    group.join_all();

    std::for_each(vec.begin(), vec.end(), std::cout << boost::lambda::_1 << "\n");
}

実行結果

20
19
19
19
20
19
19
19
19
19
19
20
19
20
20
19
19
19
19
19
20
21
22
23
24
25
26
27
28
29

2つのスレッドでスレッドセーフでない std::vector を同時に操作しているのに、値が壊れたりしていません。


しかしメソッドを書くのがすごく面倒なので、多分自分もあまり使わないような気が...。
C++0x さえあればすごく簡単に書けるはずなのにっ!