Boost.Asio Basics
This section doesn't rush into serial ports, TCP, and UDP, but instead clearly explains the basics of
io_context,run(),post(),work_guard,buffer,std::bind, and object lifecycle. If you don't understand these things, later on, serial port and network programs will encounter issues like "why the callback doesn't execute", "why the program exits directly", "why a segfault occurs", etc.
Example 1: When io_context has no tasks, run() returns immediately.
program objective
Verify a very important phenomenon: if there are no uncompleted tasks in io_context, io.run() will return immediately.
Full code
#include <boost/asio.hpp>
#include <iostream>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// io_context 是 Asio 的事件循环对象,异步任务需要靠它调度。
boost::asio::io_context io;
std::cout << "main:准备调用 io.run()" << std::endl;
// 启动事件循环,前面注册的异步任务会在这里被调度执行。
std::size_t count = io.run();
std::cout << "main:io.run() 返回,执行了 " << count << " 个回调" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo1_empty_run.cpp -o demo1_empty_run -std=c++17 -lboost_system -pthread
./demo1_empty_run
Execution output and chronological order
This program does not wait and outputs immediately:
main:准备调用 io.run()
main:io.run() 返回,执行了 0 个回调
Points to note for this example
io.run() is not always blocking. The condition for it to block is:
io_context 里还有未完成的异步任务,或者还有 work_guard 保持它不退出。
If there are no tasks, it will return immediately.
io.run() return value
io.run() The return value indicates how many handlers were executed.
In this example, there is no task, so the return value is:
0
Example 2: Submitting tasks using post() in a normal main()
program objective
boost::asio::post() can submit a normal function to io_context to be executed by io.run() later.
Full code
#include <boost/asio.hpp>
#include <functional>
#include <iostream>
#include <string>
void print_msg(const std::string& msg)
{
std::cout << "执行任务:" << msg << std::endl;
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// io_context 是 Asio 的事件循环对象,异步任务需要靠它调度。
boost::asio::io_context io;
std::cout << "main:投递任务 A" << std::endl;
// post 只是把任务放进队列,真正执行要等 io.run()。
boost::asio::post(io, std::bind(print_msg, std::string("A")));
std::cout << "main:投递任务 B" << std::endl;
boost::asio::post(io, std::bind(print_msg, std::string("B")));
std::cout << "main:准备调用 io.run()" << std::endl;
// 启动事件循环,前面注册的异步任务会在这里被调度执行。
std::size_t count = io.run();
std::cout << "main:io.run() 返回,执行了 " << count << " 个任务" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo2_post.cpp -o demo2_post -std=c++17 -lboost_system -pthread
./demo2_post
Execution output and chronological order
The program immediately outputs:
main:投递任务 A
main:投递任务 B
main:准备调用 io.run()
执行任务:A
执行任务:B
main:io.run() 返回,执行了 2 个任务
Points to note for this example
post() is not an immediately executed function, but rather puts the function into the task queue of io_context.
The actual execution occurs:
io.run();
post() Function Description
boost::asio::post(io, handler);
Parameter:
io: which task is delivered toio_context;handler: function object to be executed in the future.
Return value: Usually not a concern.
std::bind Description
std::bind(print_msg, std::string("A"))
Indicates generating a "parameterless function object". When it is executed later, it is equivalent to executing:
print_msg("A");
Example 3: work_guard prevents io.run() from immediately exiting due to lack of tasks.
program objective
In many robot programs, the Asio communication thread needs to run for a long time.
If there are no tasks for the time being, io.run() may return directly, and the communication thread ends.
executor_work_guard can tell io_context:
先别退出,我后面可能还会投递任务。
Full code
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
using WorkGuard = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
void release_guard(const boost::system::error_code& ec,
std::shared_ptr<WorkGuard> guard)
{
if (ec)
{
std::cout << "release_guard 定时器取消:" << ec.message() << std::endl;
return;
}
std::cout << "2 秒到了:释放 work_guard" << std::endl;
guard->reset();
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// io_context 是 Asio 的事件循环对象,异步任务需要靠它调度。
boost::asio::io_context io;
std::shared_ptr<WorkGuard> guard =
std::make_shared<WorkGuard>(boost::asio::make_work_guard(io));
// 创建定时器,并设置到期时间。
boost::asio::steady_timer timer(io, std::chrono::seconds(2));
// 注册异步等待:这一行不会阻塞,回调会在定时器到期后执行。
timer.async_wait(std::bind(release_guard,
std::placeholders::_1,
guard));
std::cout << "main:有 work_guard,io.run() 不会空转退出" << std::endl;
std::size_t count = io.run();
std::cout << "main:io.run() 返回,执行了 " << count << " 个回调" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo3_work_guard.cpp -o demo3_work_guard -std=c++17 -lboost_system -pthread
./demo3_work_guard
Execution output and chronological order
The program immediately outputs:
main:有 work_guard,io.run() 不会空转退出
Output in about 2 seconds:
2 秒到了:释放 work_guard
main:io.run() 返回,执行了 1 个回调
请提供您要翻译的简体中文 Markdown 片段。
main:有 work_guard,io.run() 不会空转退出
2 秒到了:释放 work_guard
main:io.run() 返回,执行了 1 个回调
Points to note for this example
If there is only work_guard, but no timer to release it, then io.run() will never return.
This is often done in the robot communication thread:
创建 io_context
创建 work_guard
开一个线程 run()
程序退出时 reset guard + stop io_context
make_work_guard() Description
boost::asio::make_work_guard(io)
Purpose: Create a guard so that io_context thinks "there is still unfinished work."
After guard.reset(), if there are no other tasks at this time, io.run() can return.
Example 4: Ordinary function parameter binding of std::bind
program objective
Practice specifically on std::bind, and clearly distinguish _1, fixed parameters, and parameter order.
Full code
#include <functional>
#include <iostream>
#include <string>
void print_robot_state(const std::string& name, double x, double y)
{
std::cout << "机器人:" << name << ",x = " << x << ",y = " << y << std::endl;
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// std::function 可以保存普通函数、lambda 或函数对象。
std::function<void(double, double)> f =
// bind 会把函数和部分参数提前绑定成一个可调用对象。
std::bind(print_robot_state,
std::string("mycar"),
std::placeholders::_1,
std::placeholders::_2);
std::cout << "main:准备调用绑定后的函数" << std::endl;
f(1.2, 3.4);
f(5.6, 7.8);
std::cout << "main:结束" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo4_bind_basic.cpp -o demo4_bind_basic -std=c++17
./demo4_bind_basic
Run output
main:准备调用绑定后的函数
机器人:mycar,x = 1.2,y = 3.4
机器人:mycar,x = 5.6,y = 7.8
main:结束
Points to note for this example
This sentence:
std::bind(print_robot_state,
std::string("mycar"),
std::placeholders::_1,
std::placeholders::_2)
Indicates:
第 1 个参数固定成 "mycar"
未来传入的第 1 个参数放到 x
未来传入的第 2 个参数放到 y
So:
f(1.2, 3.4);
Equivalent to:
print_robot_state("mycar", 1.2, 3.4);
Example 5: Class member function using std::bind
program objective
Understand why member functions are written:
&ClassName::function_name
this
Full code
#include <functional>
#include <iostream>
#include <string>
class Robot
{
public:
explicit Robot(const std::string& name)
: name_(name)
{
}
void print_pose(double x, double y)
{
std::cout << "机器人:" << name_ << ",x = " << x << ",y = " << y << std::endl;
}
private:
std::string name_;
};
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
Robot robot("mycar");
// std::function 可以保存普通函数、lambda 或函数对象。
std::function<void(double, double)> f =
// bind 会把函数和部分参数提前绑定成一个可调用对象。
std::bind(&Robot::print_pose,
&robot,
std::placeholders::_1,
std::placeholders::_2);
std::cout << "main:准备调用成员函数绑定对象" << std::endl;
f(1.0, 2.0);
f(3.0, 4.0);
std::cout << "main:结束" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo5_bind_member.cpp -o demo5_bind_member -std=c++17
./demo5_bind_member
Run output
main:准备调用成员函数绑定对象
机器人:mycar,x = 1,y = 2
机器人:mycar,x = 3,y = 4
main:结束
Points to note for this example
Regular function binding:
std::bind(print_robot_state, ...)
Member function binding:
std::bind(&Robot::print_pose, &robot, ...)
Reason: Member functions must be called on an object.
What is &Robot::print_pose?
It is a member function pointer, representing the print_pose function in the Robot class.
But it hasn't specified a specific object yet.
What is &robot?
It indicates which object's member function is being called.
So the full meaning is:
未来调用 robot.print_pose(x, y)。
Example 6: Basic Usage of buffer
program objective
When reading and writing data with Boost.Asio, one often writes:
boost::asio::buffer(data)
This example first examines its basic meaning.
Full code
#include <boost/asio.hpp>
#include <array>
#include <iostream>
#include <string>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
std::string msg = "hello asio";
auto buf1 = boost::asio::buffer(msg);
// std::array 是固定长度数组,长度在编译期就确定。
std::array<char, 128> data;
auto buf2 = boost::asio::buffer(data);
std::cout << "msg.size() = " << msg.size() << std::endl;
std::cout << "buffer(msg).size() = " << buf1.size() << std::endl;
std::cout << "array size = " << data.size() << std::endl;
std::cout << "buffer(data).size() = " << buf2.size() << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo6_buffer.cpp -o demo6_buffer -std=c++17 -lboost_system -pthread
./demo6_buffer
Run output
msg.size() = 10
buffer(msg).size() = 10
array size = 128
buffer(data).size() = 128
Points to note for this example
boost::asio::buffer() typically does not copy data; it just creates a "buffer view pointing to existing memory."
Therefore, pay special attention when performing asynchronous reads and writes:
buffer 指向的原始数据必须活到异步回调完成。
Example of incorrect writing:
void send()
{
std::string msg = "hello";
boost::asio::async_write(socket_, boost::asio::buffer(msg), handler);
}
This msg is destroyed right after send() returns, but the asynchronous write may not have completed yet.
Correct approach:
把 msg 做成类成员变量
或者用 shared_ptr 管理
或者使用写队列保存待发送数据
Example 7: Encapsulating the io_context worker thread in a class
program objective
In engineering, it is often desired to run Asio in a separate thread.
This example creates a minimal IoThread class:
- Created during construction
work_guard; start()start a thread to executeio.run();stop()Stop thread;- Use
post()to submit tasks and verify the results.
Full code
#include <boost/asio.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
class IoThread
{
public:
IoThread()
: guard_(boost::asio::make_work_guard(io_))
{
}
~IoThread()
{
stop();
}
void start()
{
thread_ = std::thread(std::bind(&IoThread::run, this));
}
void stop()
{
if (!stopped_)
{
stopped_ = true;
guard_.reset();
io_.stop();
if (thread_.joinable())
{
thread_.join();
}
}
}
boost::asio::io_context& io()
{
return io_;
}
private:
void run()
{
std::cout << "IoThread:io.run() 开始" << std::endl;
// 启动事件循环,前面注册的异步任务会在这里被调度执行。
io_.run();
std::cout << "IoThread:io.run() 返回" << std::endl;
}
private:
// io_context 是 Asio 的事件循环对象,异步任务需要靠它调度。
boost::asio::io_context io_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
std::thread thread_;
bool stopped_ = false;
};
void print_task(const std::string& msg)
{
std::cout << "执行任务:" << msg << std::endl;
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
IoThread io_thread;
io_thread.start();
// post 只是把任务放进队列,真正执行要等 io.run()。
boost::asio::post(io_thread.io(), std::bind(print_task, std::string("A")));
boost::asio::post(io_thread.io(), std::bind(print_task, std::string("B")));
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "main:准备停止 IoThread" << std::endl;
io_thread.stop();
std::cout << "main:结束" << std::endl;
return 0;
}
运行结果:见下方“运行输出与时间顺序”;如果示例涉及定时器、线程、网络或外部设备,具体时间和顺序可能会随环境略有变化。
Compile and run
g++ demo7_iothread.cpp -o demo7_iothread -std=c++17 -lboost_system -pthread
./demo7_iothread
Execution output and chronological order
After the program starts, the worker thread begins running:
IoThread:io.run() 开始
执行任务:A
执行任务:B
After about 1 second, the main thread stops it:
main:准备停止 IoThread
IoThread:io.run() 返回
main:结束
请提供您要翻译的简体中文 Markdown 片段。
IoThread:io.run() 开始
执行任务:A
执行任务:B
main:准备停止 IoThread
IoThread:io.run() 返回
main:结束
Points to note for this example
This class is the prototype for encapsulating serial port, TCP, and UDP communication threads in the future.
However, note:
io_.stop();
will cause io_context to stop as soon as possible, and unfinished asynchronous tasks may not complete normally.
A more detailed approach in the project is:
先 cancel socket / timer / serial_port
再 reset work_guard
最后等待 run() 返回
Section Summary
- When there is no task,
io.run()returns immediately. post()can submit ordinary functions toio_contextfor execution.work_guardcan preventio.run()from exiting because there are no tasks to perform.std::bindcan bind ordinary functions, as well as member functions.boost::asio::buffer()is usually just a memory view and is not responsible for extending the data lifecycle.- The most important aspects in asynchronous engineering are object lifecycle and buffer lifecycle.