Hatena::ブログ(Diary)

Faith and Brave - C++で遊ぼう このページをアンテナに追加 RSSフィード Twitter

2011-09-13

[] Boost.Asio postとdispatchの違い

Boost.Asioのio_serviceには、post()とdispatch()というほぼ同じことをする関数が用意されています。

以下のブログでわかりやすく解説されていたので、それを見ていくことにします。


To post or to dispatch? - This Thread


まず、io_serviceを複数スレッドで動作させます。

それと、最初にメインスレッドのIDを出力しておきます。

std::cout << boost::this_thread::get_id() << std::endl;

asio::io_service io_service;
asio::io_service::work work(io_service);

boost::thread_group group;
const int count = 3;
for (int i = 0; i < count; ++i) {
    group.create_thread(boost::bind(&asio::io_service::run, &io_service));
}

複数スレッドで実行されているio_serviceに同じ関数をpost()します。

ここのpost()は、post()/dispatch()の違いには直接関係しません(間接的には関係ある)。

while (startAFunction()) {
    io_service.post(boost::bind(fA, boost::ref(io_service)));
}

io_serviceにpost()された関数の中では、post()とdispatch()を交互に呼び出すようにしています。

post()とdispatch()はそれぞれ同じ関数を呼び出します。ここでも現在のスレッドのIDを出力します。

void fA(asio::io_service& io_service)
{
    static int selector = 0;

    ++selector;
    if (selector % 2 == 0) {
        print(boost::this_thread::get_id(), " post");
        io_service.post(fB);
    }
    else {
        print(boost::this_thread::get_id(), " dispatch");
        io_service.dispatch(fB);
    }
}

post()とdispatch()によって呼ばれる関数fBは、単に現在のスレッドのIDを出力しているだけです。

void fB()
{
    print(boost::this_thread::get_id(), " call fB");
}

これを実行すると以下のような結果になります(読みやすいように一部編集しています):

00154EE0

00154FB8 dispatch
00154FB8 call fB

00154FB8 post
00154F58 call fB

00154F58 dispatch
00154F58 call fB

dispatch()によって呼ばれた関数は、dispatch()されたのと同じスレッドで呼ばれていて、

post()の場合は異なるスレッドで呼ばれていることがわかります。


dispatch()は、呼び出し元の関数が非同期に実行されている場合に非同期ではなく直接その関数を呼び出し、そうでなければpost()と同様にキューに登録して非同期に実行する、という挙動をします。

そのため、シングルスレッドでio_serviceを動かしている場合にはpost()とdispatch()に違いはなく、マルチスレッドで動かしたときのみdispatch()が特定の状況下において効率的になり得ます。

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

namespace asio = boost::asio;

boost::mutex mutex;
void print(boost::thread::id id, const std::string& s)
{
    boost::mutex::scoped_lock lock(mutex);
    std::cout << id << s << std::endl;
}

void fB()
{
    print(boost::this_thread::get_id(), " call fB");
}

void fA(asio::io_service& io_service)
{
    static int selector = 0;

    ++selector;
    if (selector % 2 == 0) {
        print(boost::this_thread::get_id(), " post");
        io_service.post(fB);
    }
    else {
        print(boost::this_thread::get_id(), " dispatch");
        io_service.dispatch(fB);
    }
}

bool startAFunction()
{
    print(boost::this_thread::get_id(), " Enter a non-empty string to run A function");

    std::string input;
    getline(std::cin, input);
    return input.length() == 0 ? false : true;
}

int main()
{
    std::cout << boost::this_thread::get_id() << std::endl;

    asio::io_service io_service;
    asio::io_service::work work(io_service);

    boost::thread_group group;
    const int count = 3;
    for (int i = 0; i < count; ++i) {
        group.create_thread(boost::bind(&asio::io_service::run, &io_service));
    }

    while (startAFunction()) {
        io_service.post(boost::bind(fA, boost::ref(io_service)));
    }

    std::cout << "stop io_service" << std::endl;
    io_service.stop();
    group.join_all();

    std::cout << "end application" << std::endl;
}

CryoliteCryolite 2011/09/13 23:02 >シングルスレッドでio_serviceを動かしている場合にはpost()とdispatch()に違いはなく

ハンドラ内から呼び出した場合,シングルスレッド下でも違いは生じます.

dispatch した場合はハンドラ関数のコールスタックがどんどん深くなっていく一方で, post した場合は post を呼んだハンドラ関数から制御フローがいったん io_service 内のメッセージループに戻った後で, post されたハンドラ関数が呼ばれますのでコールスタックは深くなりません.

あるイベントハンドラが別のイベントハンドラを呼び出す場合で,特にあるイベントハンドラが直接または間接に自分自身を再び呼び出すこともあり得なくはないので,この場合には post と dispatch に決定的な違いが出てきます.自分自身を直接または間接に呼び出す場合には dispatch ではどこかでスタックオーバーフローが起こりえます.

ハンドラが別のハンドラを呼び出す関係をコールツリーのように見立てた場合,おおまかにいって dispatch だけでやると深さ優先,post だけでやると幅優先にコールツリーをたどっていくようになります.深さ優先の状態を記録するスタックはコールスタックとして,幅優先の状態を記録するキューは io_service 内のイベントキューとして,各々保持されているものとみなせます.

どちらが良いのかは文脈によるとは思いますが,一般には dispatch のほうがコンテキストが切り替わらず,また内容が関連したハンドラが連続して呼ばれて参照局所性が維持される傾向にあるのではないかと思います.

faith_and_bravefaith_and_brave 2011/09/13 23:05 なるほど!たしかにそうです。

mannymanny 2011/09/14 07:43 Hey, thank you for translating this in Japanese! Any feedback, correction, integration welcomed as comment to my original post. Better if in English ;-)

faith_and_bravefaith_and_brave 2011/09/14 11:23 Hi, manny

Thanks for great post.
This is very useful!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/faith_and_brave/20110913/1315895805