Boost.Asioによるワーカースレッドパターン


Boost によるデザインパターン - Worker Thread


とのことだったので書いてみました。

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>

class thread_pool {
    boost::asio::io_service& io_service_;
    boost::shared_ptr<boost::asio::io_service::work> work_;
    boost::thread_group group_;
public:
    thread_pool(boost::asio::io_service& io_service, std::size_t size)
        : io_service_(io_service)
    {
        work_.reset(new boost::asio::io_service::work(io_service_));

        for (std::size_t i = 0; i < size; ++i) {
            group_.create_thread(boost::bind(&boost::asio::io_service::run, &io_service_));
        }
    }

    ~thread_pool()
    {
        work_.reset();
        group_.join_all();
    }

    template <class F>
    void post(F f)
    {
        io_service_.post(f);
    }
};

// test
#include <iostream>
#include <string>
#include <boost/format.hpp>

boost::mutex g_mutex;

void func(const std::string& str, int num, int count)
{
    int n = 0;
    for (int i = 0; i < num; i++)
    {
        n += count;
        {
            boost::mutex::scoped_lock lock(g_mutex);
            std::cout << (boost::format("%1% %2%/%3% (%4%)") % str % i % num % ::GetCurrentThreadId()) << std::endl;
        }
        ::Sleep(1000);
    }
    {
        boost::mutex::scoped_lock lock(g_mutex);
        std::cout << (boost::format("%1% %2%/%2% (%3%) result: %4%") % str % num % ::GetCurrentThreadId() % n) << std::endl;
    }
}


class hoge
{
private:
    int num_;
    int count_;

public:
    hoge(int num, int count) : num_(num), count_(count) { }
    void func(const std::string& str) { ::func(str, num_, count_); }
};

int main()
{
    boost::asio::io_service io_service;
    thread_pool tp(io_service, 3);

    tp.post(boost::bind(func, "request1:", 5, 10));
    tp.post(boost::bind(func, "request2:", 5, 10));
    ::Sleep(3000);
    hoge h(3, 5);
    tp.post(boost::bind(&hoge::func, &h, "request3:"));
    tp.post(boost::bind(&hoge::func, &h, "request4:"));
    ::Sleep(7000);
}

実行結果:

request1: 0/5 (3948)
request2: 0/5 (3312)
request1: 1/5 (3948)
request2: 1/5 (3312)
request1: 2/5 (3948)
request2: 2/5 (3312)
request3: 0/3 (264)                // スレッドプールの空きがないので request4 は開始しない
request1: 3/5 (3948)
request2: 3/5 (3312)
request3: 1/3 (264)
request1: 4/5 (3948)
request2: 4/5 (3312)
request3: 2/3 (264)
request1: 5/5 (3948) result: 50
request2: 5/5 (3312) result: 50
request4: 0/3 (3948)               // スレッドプールの空きが出来たので request4 が開始された
request3: 3/3 (264) result: 15
request4: 1/3 (3948)
request4: 2/3 (3948)
request4: 3/3 (3948) result: 15

キューの管理をboost::asio::io_serviceに任せることができて楽チンですね。



追記:
io_service::workがないと、キューが空になった時点でrun()が終わってしまうようです。
全ての処理が行われてくれないと困りますね・・・。workを追加しました。


修正履歴:
2011/04/08 17:16 : workを使ってなかったので修正。