简介
boost的asio采用的是Proactor模型,该模型的核心思想就是异步IO,IO在事件循环中,每个异步IO都绑定对应的回调函数,当IO完成后,对应的回调函数会在事件循环中执行。
异步并发模型说明
一个TcpServer监听指定的端口,当有客户端请求到来时,会建立一个与之对应的会话,会话的生命周期与用户连接的生命周期一致。
每个异步IO,我们都绑定了一个与之对应的回调函数,这样异步完成之后,会立刻执行对应的回调函数。注意std::enable_from_this
的使用方式,为了在回调中使用对应的数据,同时也为了可以智能地管理生命周期,我们直接在异步函数中获取对象本身的智能指针,该类及其对应的方法可以帮助我们实现目的,具体参考代码。
io_context
可以理解为一个事件循环,当事件循环上有事件时,执行io_context::run()
的线程就会获取对应的事件回调函数,并执行。如果要一个事件循环有多个线程并发执行回调,需要并发执行run
函数即可,具体参考main.cpp的方式。io_context::run()
如果没有对应的任务,则会自动退出,我们使用work_guard
的模式,使之始终运行。
该框架是一个最基础的异步IO的Proactor模型,更复杂的业务逻辑,也只需要在对应的模型下,添加有关的组件即可。
注意,下面的代码不是一个安全的并发模型,这里只是讲了一个代码示例而已,简化复杂度;异步回调写给客户端数据的时候,会有写竞争的情况出现。
代码示例
基于boost 1.70和C++14
CMakeLists.txt
# cmake_minimum_required(VERSION <specify CMake version here>)
project(boost_asio)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS -pthread)
add_executable(boost_asio main.cpp TcpServer.hpp Session.hpp)
Session.hpp
//
// Created by Erick on 2020/2/19.
//
#ifndef BOOST_ASIO_SESSION_HPP
#define BOOST_ASIO_SESSION_HPP
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
class Session : public std::enable_shared_from_this<Session> {
using SessionSocketPtr = std::shared_ptr<boost::asio::ip::tcp::socket>;
public:
explicit Session(SessionSocketPtr sk) : m_socket(std::move(sk)) {}
void start() {
boost::asio::async_read_until(*m_socket, m_streamBuf, "\r\n",
[self = shared_from_this()](const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if (ec.failed()) {
std::cout << "session error: " << ec.message() << ", thread_id: "
<< std::this_thread::get_id() << std::endl;
return;
}
std::cout << "Thread: " << std::this_thread::get_id() << ", Get User data: "
<< std::istream(&self->m_streamBuf).rdbuf()
<< std::endl;
self->start(); // 异步继续读
});
}
private:
SessionSocketPtr m_socket;
boost::asio::streambuf m_streamBuf;
};
#endif //BOOST_ASIO_SESSION_HPP
TcpServer.hpp
//
// Created by Erick on 2020/2/19.
//
#ifndef BOOST_ASIO_TCPSERVER_HPP
#define BOOST_ASIO_TCPSERVER_HPP
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include "Session.hpp"
// 异步的TCP服务器
class TcpServer : public std::enable_shared_from_this<TcpServer> {
public:
explicit TcpServer(boost::asio::io_context &ioc, int port) :
io_context(ioc),
m_acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {
std::cout << "start TcpServer...\n";
}
TcpServer(TcpServer &) = delete;
TcpServer(TcpServer &&) = delete;
void start() {
auto socket = std::make_shared<boost::asio::ip::tcp::socket>(io_context);
m_acceptor.async_accept(*socket,
[socket, self = shared_from_this()](boost::system::error_code ec) {
if (ec.failed()) {
std::cout << "async_accept error: " << ec.message() << std::endl;
}
auto session = std::make_shared<Session>(socket);
session->start(); // 这里启动一个会话
std::cout << "thread_id: " << std::this_thread::get_id() << ", create a session\n";
self->start(); // 继续重新启动
});
}
private:
boost::asio::io_context &io_context;
boost::asio::ip::tcp::acceptor m_acceptor;
};
#endif //BOOST_ASIO_TCPSERVER_HPP
main.cpp
#include <iostream>
#include <thread>
#include "TcpServer.hpp"
const auto N = std::thread::hardware_concurrency();
using work_guard_type = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
int main() {
std::cout << "begin asio model" << std::endl;
boost::asio::io_context io_context;
work_guard_type work_guard(io_context.get_executor()); // 启动work_guard,即使没有任务也不停止io_context
std::vector<std::thread> threads;
for (auto i = 0; i < N; ++i) {
threads.emplace_back(std::thread([&]() {
io_context.run();
}));
}
int port;
std::cout << "input port: ";
std::cin >> port;
auto tcpServer = std::make_shared<TcpServer>(io_context, port);
tcpServer->start(); // 异步启动服务器,不阻塞
for (auto &t: threads) {
if (t.joinable()) {
t.join();
}
}
std::cout << "end asio model" << std::endl;
return 0;
}
参考资料:
- https://dens.website/tutorials/cpp-asio/multithreading-2