BoostAsioで可読性を求めるのは間違っているだろうか
- 5. Boost.Asioとは
Asynchronous I/O。proactorパターン
シングルスレッドでI/Oを非同期で処理する
従来のmultithreadパターンだと、threadのコストが高
く、大量のクライアントを さばけない(C10K問題)
カーネルにread/write命令を送り、完了時にコール
バックさせるselect/epoll等のnonblocking I/Oと比較し
て、user/kernelのコンテキストスイッチが少なくなる
と思われる
LinuxではepollでProacotr実装している模様
関数型プログラミング勉強してみたかったので、コー
ルバック地獄は本望だ
ラムダたん
- 9. stackless coroutine
簡単そうだったのでこちらから
asio::coroutineを継承する
適用範囲は #include
<boost/asio/yield.hpp> ~ #include
<boost/asio/unyield.hpp> で囲む
coroutine関数は operator()に
operator()以外でも出来そうだけど調べてない
coroutine部分は reenter()で囲む
yield fork等をキーワードのように使え
る
- 10. header
asio::coroutineを継承する事
class server : boost::asio::coroutine {
コールバック先の関数オブジェクト
void operator()(ほげ)
fork時にコンストラクタが呼ばれないので、
shared_ptr等を使うと初期化しやすい(指摘あれ
ばよろしくお願いします)
std::shared_ptr<tcp::acceptor> acceptor_;
std::shared_ptr<tcp::socket> socket_;
std::shared_ptr<std::array<char, 1024> > buffer_;
- 14. 例
// coroutineを使わない例
asio::async_read(socket_, asio::buffer(buffer_),
[this](boost::system::error_code ec, std::size_t ){
asio::async_write(socket_, asio::buffer(buffer_),
[this](boost::system::error_code ec, std::size_t ){
socket_.shutdown(tcp::socket::shutdown_both, ec);
});
}
});
// stackless_coroutineを使った例
reenter(this){
do{
yield acceptor_->async_accept(socket_,*this);
fork server(*this)();
}while(is_parent());
yield socket_.async_read_some(asio::buffer(buffer_), *this);
yield socket.async_write_some(asio::buffer(buffer_), *this);
socket_.shutdown(tcp::socket::shutdown_both, ec);
};
可読性あがりましたよね?
- 17. 参考
stackless coroutine Overview
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/overvie
w/core/coroutine.html
AsioSample HTTP server4
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/exampl
es/cpp03_examples.html
- 18. stackful coroutine
依存ライブラリ
./b2 --with-system --with-thread --with-
date_time --with-regex --with-serialization
asio::spawn()に、コルーチンを行いたい関数
(オブジェクト)を指定しyield_contextを取得
する
Stacklessと違い、スタックコンテキストを個
別に作成するので、ローカル変数も可能
Asynchronous命令のコールバックに
yield_contextを指定する
意外に簡単そう
- 19. spawn
spawnにより yield_contextを作成する
複数の接続を受ける場合は connection毎に spawnする必要がある
boost::asio::spawn(io_service, [&] (boost::asio::yield_context yield) {
…
for (;;) {
tcp::socket _socket(io_service);
acceptor.async_accept(_socket, yield);
// C++14の汎用ラムダキャプチャ欲しい・・
asio::spawn(strand_, [_socket](asio::yield_context yield_child) {
tcp::socket socket(std::move(_socket));
….. yield_child を使う!
});
}
}
- 22. 例
// coroutineを使わない例
asio::async_read(socket_, asio::buffer(buffer_),
[this](boost::system::error_code ec, std::size_t ){
asio::async_write(socket_, asio::buffer(buffer_),
[this](boost::system::error_code ec, std::size_t ){
socket_.shutdown(tcp::socket::shutdown_both, ec);
});
}
});
// coroutineを使った例
boost::asio::spawn(strand_, [this, self](boost::asio::yield_context yield){
boost::system::error_code ec;
socket_.async_read_some(asio::buffer(buffer_), yield[ec]);
socket.async_write_some(asio::buffer(buffer_), yield[ec]);
socket_.shutdown(tcp::socket::shutdown_both, ec);
});
可読性あがりましたよね?
- 23. 参考
stackful coroutine Overview
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/overvie
w/core/spawn.html
Asio sample spawn
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/exampl
e/cpp11/spawn/echo_server.cpp
- 24. 有限状態マシン(finite state machine)
通信に限らずStateパターンを使う事は
多いが、大抵はこの部分の可読性が悪
くなる
一般的にはenumでモードを設定しint型
と相互変換して使ったり、switch caseあ
るいは関数ポインタ配列で実装したり
State変更関数作ったり・・
大抵は Stateがネストしたり、State変更
条件が色々あったり・・・
それらが大幅に簡略化されます
- 25. Boost.MSM
有限状態マシン(finite state machine)
Boost.Statechartより速い(RTTIや
virtualを使用してないっぽい)
Boost::MPLを使う
StateとEventを作成する
transition_tableを作成し、Eventにより
Stateを変更できる
ガード、アクションを指定出来る
- 26. Boost.MSM
// 状態作成
struct start : public boost::msm::front::state<>{};
struct end : public boost::msm::front::state<>{};
// イベント作成
struct event_goal{};
// イベントテーブル作成
struct state : public msm::front::state_machene_def<state>
{
struct table : mpl::vector<
_row<start, event_goal, end> // goalイベントが来たら endへ遷移
>
….
}
state st;
st.process_event(goal()); // goalイベント呼ばれたので endへ遷
移
- 29. shared_ptrキャプチャ
class session{
void do_read(){
async_read( socket_,buffer,[this](){this->hoge;});
}
}
非同期なのでハンドラ実行時にthisは開放されているかもしれない
ハンドラを実行しようと心の中で思ったならッ!その時スデにオブ
ジェクトは終わってるんだッ!
class session::enable_shared_from_this<session>{
void do_read(){
auto self(shared_from_this());
async_read( socket_,buffer,[this, self](){this->hoge;});
}
}
自分のshared_ptrをキャプチャし、ハンドラ終了までオブジェクトを
終了させない