在使用coroutine+asio多線程框架的時候,如何維護連接池復用連接?


我發現這是Boost.ASIO+Boost.Coroutine的一個常見問題。

在這種情況下,你需要自己實現鎖並調度coroutine,大致的思路是如果「鎖」阻塞,則將當前coroutine掛起,當「鎖」可用時再重新激活被阻塞的coroutine。

如果你懶得折騰,可以用我寫的Fiberized.IO,在fibio/mutex.cpp和fibio/condition.cpp中你可以看到前述方法的一個具體實現。


因為是非同步,那麼連接池可以理解成一個socket容器,如果只針對容器的相關操作加鎖,那麼就不應該導致非同步阻塞,這跟協程沒有什麼關係。如果使用的是asio::spawn的話,寫起來就更簡單了。

首先搞個連接池,其中pull函數就帶有一個協程出讓點

class ConnectionManager {
public:
template & tcp::socket pull(Yield yield)
{
{
lock_guard& lock{m_};
if (!connections_.empty()) {
auto s = move(connections_.front());
connections_.pop();
return s;
}
}

auto s = tcp::socket(io_);
// 上面可能是#1線程
s.async_connect({host, port}, yield); // 出讓點
// 這裡可能是#2線程
return s;
}

void push(tcp::socket s)
{
lock_guard& lock{m_};
connections_.push(move(s));
}

private:
asio::io_service io_;
queue& connections_;
mutex m_;
};

發送端就用定時器簡單寫下

template & void sender(Yield yield)
{
while (true) {
auto socket = mgr.pull(yield); // 出讓點
ON_EXIT([] { mgr.push(socket); });

asio::system_timer timer{ios};
timer.expires_from_now(1s);

timer.async_wait(yield); // 出讓點
asio::async_write(socket, asio::buffer(L「隨便寫點」), yield); // 出讓點
socket.async_read_some(asio::buffer(buf), yield); // 出讓點
}
}

最後構建線程池

for (int i = 0; i &< CO_SIZE; ++i) { asio::spawn(ios, [](auto yield) { sender(yield); }); // 創建多個發送協程 } vector& thds;
generate_n(back_inserter(thds), TH_SIZE, [] { // 創建多個工作線程
return thread{[] {
try {
io.run();
}
catch (...) {
// handle exception
}
}};
});
for_each(thds.begin(), thds.end(), [](auto t) { t.join(); });

至於中間的調度,Asio和Coroutine已經搞定了,所以不用自己動手。


推薦閱讀:

Linux C++怎麼做框架的性能調優?

TAG:Linux | 伺服器 | 多線程 | BoostC庫 | 協程 |