2013-04-11 | 13:40
自分が生成したスレッドからのコールバックを受け取る方法
Listener方式
コールバック関数が定義されているListenerクラスを作成し、コールバックを受け取る側のクラスでListenerを継承する。
#include <iostream>
#include <functional>
#include <boost/function.hpp>
#include <boost/thread.hpp>
class Listener{
public:
virtual void callback()=0;
};
class Child{ // (Listenerについて知っていれば)Parentのことは直接知らなくても良い
public:
void set_listener(Listener* p){
listener_ = p;
}
void bar(){
std::cout << "Child::bar(" << boost::this_thread::get_id() << ")" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
// Listenerのcallback()を呼び出す
listener_->callback();
}
private:
Listener* listener_;
};
// Listenerを継承(implement)する
class Parent : public Listener{
public:
void foo(){
// Listenerとして自分を登録しておいて
child_.set_listener(this);
// bar()を別スレッドで呼び出す --- (1)
boost::thread t([this]()->void{
this->child_.bar();
});
}
void callback(){
std::cout << "Parent::callback(" << boost::this_thread::get_id() << ")" << std::endl;
}
private:
Child child_;
};
int main(){
std::cout << "main start(" << boost::this_thread::get_id() << ")" << std::endl;
Parent parent;
parent.foo();
std::cout << "Parent::foo() called" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
std::cout << "exit." << std::endl;
return 0;
}
スレッドにコールバック関数を登録しておく方式
この方法だと、コールバックを受け取る側はListenerを継承する必要はない。
#include <iostream>
#include <boost/function.hpp>
#include <boost/thread.hpp>
class Child{
public:
// コールバック関数を登録
void set_callback(boost::function<void (void)>& f){
callback_ = f;
}
void bar(){
std::cout << "Child::bar(" << boost::this_thread::get_id() << ")" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
// callbackを呼び出す
callback_();
}
private:
boost::function<void (void)> callback_;
};
class Parent {
public:
void foo(){
// callback()をchild_のコールバック関数として登録
boost::function<void (void)> func = boost::bind(&Parent::callback, this);
child_.set_callback(func);
// bar()を別スレッドで呼び出す --- (1)
std::cout << "I will call Child::bar()(" << boost::this_thread::get_id() << ")" << std::endl;
boost::thread t([this]()->void{
this->child_.bar();
});
}
void callback(){
std::cout << "Parent::callback(" << boost::this_thread::get_id() << ")" << std::endl;
}
private:
Child child_;
};
int main(){
std::cout << "main start(" << boost::this_thread::get_id() << ")" << std::endl;
Parent parent;
parent.foo();
std::cout << "Parent::foo() called" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
std::cout << "exit." << std::endl;
return 0;
}
boost::signals2を使う
#include <iostream>
#include <boost/function.hpp>
#include <boost/signals2.hpp>
#include <boost/thread.hpp>
class Child{
public:
// コールバック関数を登録
void set_callback(boost::function<void (void)>& f){
sig_.connect(f);
}
void bar(){
std::cout << "Child::bar(" << boost::this_thread::get_id() << ")" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
// signalを送る
sig_();
}
private:
boost::signals2::signal<void (void)> sig_;
};
class Parent {
public:
void foo(){
// callback()をchild_のコールバック関数として登録
boost::function<void (void)> func = boost::bind(&Parent::callback, this);
child_.set_callback(func);
// bar()を別スレッドで呼び出す --- (1)
std::cout << "I will call Child::bar()(" << boost::this_thread::get_id() << ")" << std::endl;
boost::thread t([this](){
this->child_.bar();
});
}
void callback(){
std::cout << "Parent::callback(" << boost::this_thread::get_id() << ")" << std::endl;
}
private:
Child child_;
};
int main(){
std::cout << "main start(" << boost::this_thread::get_id() << ")" << std::endl;
Parent parent;
parent.foo();
std::cout << "Parent::foo() called" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
std::cout << "exit." << std::endl;
return 0;
}
上の例ではコールバック関数は1つだけだが、signals2には複数のコールバック関数を登録できる。
コールバック関数を、呼び出し元のスレッドではなく、メインのスレッド側で実行したい。
上記の全サンプルでは、コールバック関数は(1)で生成したスレッド(以下、thread-1)上で実行される(実行時に括弧内で表示されているidを参照)。thread-1を生成した側のメインのスレッド(thread-M)とは別スレッドなので、コールバック関数が呼ばれた時に行う処理(以下、処理A)によっては、排他制御を考慮する必要がある。また、処理Aに時間がかかる場合は、その間thread-1側の処理がブロックされることになる。そこで、コールバック関数が呼ばれた後に処理Aをthread-M側で処理する方法を考える。
boost.asioを使った実装
コールバック関数内でio_serviceに処理Aをポストしておき、コールバック関数はすぐにリターンする。io_serviceにポストされた処理Aはthread-M側で実行される。
#include <iostream>
#include <boost/asio.hpp>
#include <boost/function.hpp>
#include <boost/thread.hpp>
class Child{
public:
// コールバック関数を登録
void set_callback(boost::function<void (void)>& f){
callback_ = f;
}
void bar(){
std::cout << "Child::bar(" << boost::this_thread::get_id() << ")" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
// callbackを呼び出す
callback_();
}
private:
boost::function<void (void)> callback_;
};
class Parent {
public:
void foo(){
// io_serviceのハンドラが空になっても終了しないようにする
boost::asio::io_service::work w(io_);
// callback()をchild_のコールバック関数として登録
boost::function<void (void)> func = boost::bind(&Parent::callback, this);
child_.set_callback(func);
// bar()を別スレッドで呼び出す
std::cout << "I will call Child::bar()(" << boost::this_thread::get_id() << ")" << std::endl;
boost::thread t([this]()->void{
this->child_.bar();
});
io_.run();
}
void callback(){
std::cout << "Parent::callback(" << boost::this_thread::get_id() << ")" << std::endl;
// ハンドラをポストする
io_.post([](){
// thread-M上で実行される処理
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
std::cout << "posted function(" << boost::this_thread::get_id() << ")" << std::endl;
});
std::cout << "posted(" << boost::this_thread::get_id() << ")" << std::endl;
}
private:
Child child_;
boost::asio::io_service io_;
};
int main(){
std::cout << "main start(" << boost::this_thread::get_id() << ")" << std::endl;
Parent parent;
parent.foo();
return 0;
}
スポンサーサイト
Comment
Post a comment