自分は今まで、出来る限りマルチスレッドとは関わらないようにしてきました。
曰く、マルチスレッドは100億千万回に1回しか再現しないバグがある。
曰く、マルチスレッドはライブラリにもバグがある。
曰く、マルチスレッドは実装工数が半分になってバグ工数が2乗になる。
曰く、マルチスレッドに関わると糸を見ただけで気が触れるようになる。
これだけ聞かされればそりゃマルチスレッドが怖くなるというものです。
でも最近マルチスレッドのデザインパターン本を読んで何となく理解した気になってきたので、(まんじゅうこわい的な意味で)マルチスレッドこわいです。
仕事では使うチャンスがないので家でしか遊んでませんが。
で、Boost には Boost.Thread というのがあるので、これを使って汎用的に Thread-Per-Message パターンを行うクラスを作ってみました。
Thread-Per-Message パターンは、特定の処理をスレッドにポイッと渡して処理してもらうパターンです。
時間の掛かる処理をスレッドに行ってもらうことによって、呼び出す側の応答速度が上がります。
戻り値はありません。戻り値が欲しい場合は Future パターンを使います。
class thread_function
{
private:
boost::thread_group group_;
public:
void post_message(boost::function0<void> func)
{
group_.create_thread(func);
}
void join_all() { group_.join_all(); }
};
boost::mutex g_mutex;
class hoge
{
private:
const std::string str_;
public:
hoge(std::string str) : str_(str) { }
void func(int count)
{
for (int i = 0; i < count; i++)
{
boost::mutex::scoped_lock lock(g_mutex);
std::cout << str_ << " - " << i << std::endl;
}
}
};
void func(int count, std::string str)
{
for (int i = 0; i < count; i++)
{
boost::mutex::scoped_lock lock(g_mutex);
std::cout << str << " - " << i << std::endl;
}
}
void main()
{
{
boost::mutex::scoped_lock lock(g_mutex);
std::cout << "start" << std::endl;
}
thread_function tf;
tf.post_message(boost::bind(func, 2, " ::func1"));
hoge h1("hoge::func1");
tf.post_message(boost::bind(&hoge::func, &h1, 3));
tf.post_message(boost::bind(func, 4, " ::func2"));
hoge h2("hoge::func2");
tf.post_message(boost::bind(&hoge::func, &h2, 5));
{
boost::mutex::scoped_lock lock(g_mutex);
std::cout << "end" << std::endl;
}
tf.join_all();
}
start
::func1 - 0
::func2 - 0
hoge::func1 - 0
hoge::func2 - 0
end
::func1 - 1
::func2 - 1
hoge::func1 - 1
hoge::func2 - 1
::func2 - 2
hoge::func1 - 2
hoge::func2 - 2
::func2 - 3
hoge::func2 - 3
hoge::func2 - 4
boost::function0<void> を使うことによって、どんな関数でも実行することが出来るようになっています。
Boost.Function 超便利っす。
ところで作ってみたはいいけど Thread-Per-Message パターンを実際に使用するシチュエーションというのが思い浮かばない訳ですががが。