Boost.Asio 是一個用於網絡和低級 I/O 編程的跨平台 C++ 庫,它使用現代 C++ 方法為開發人員提供一致的異步模型
一個異步使用計時器的樣例
#include <iostream>
#include <boost/asio.hpp>
void print(const boost::system::error_code & /* e */)
{
std::cout <<“hello world!” << std::endl;
}
int main()
{
boost :: asio :: io_context me; // 提供對 i/o 功能的訪問
boost :: asio :: steady_timer t(io,boost :: asio :: chrono :: seconds(5));
t.async_wait(print); // 插入回調函數
io.run();
return;
}
asio 庫提供了一種保證,即只能從當前調用 io_context::run() 的線程調用回調處理程序
io_context::run() 函數將繼續運行,它的工作是計時器上的異步等待,在計時器到期並且回調完成之前調用不會返回
在調用 io_context::run() 前需要給 io_context 設定一些工作,如果省略了 t.async_wait(print); 的調用,io_context::run() 將立刻返回
多次觸發計時器
要實現重復計時器,需要在回調函數中更改計時器的到期時間,然后啟動新的異步等待
設定計時器將在第 6 次觸發時停止程序
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
// 增加指向計時器對象的指針以訪問計時器對象,以及一個計數器 count
void print(const boost::system::error_code & /*e*/,
boost::asio::steady_timer * t, int * count)
{
// 這里沒有明確的調用要求 io_context 停止,而是通過 count == 5 時不再對計時器啟動新的異步等待,io_context 將結束工作並停止運行
if (*count < 5)
{
std::cout << *count << std::endl;
++(*count);
// 將計時器到期時間向后延時 1 秒
t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));
// 啟動一個新的異步等待,使用 boost::bind 使需要指定與回調函數參數列表相匹配的參數
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_context io;
int count = 0;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
std::cout << "Final count is " << count << std::endl;
return 0;
}
使用類的成員函數作回調處理
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
class printer
{
public:
// 構造時引用 io_context 對象,使用它初始化 timer
printer(boost::asio::io_context& io)
: timer_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
// 使用 bind 綁定當前對象的 this 指針,利用成員 count 控制計時器的執行
timer_.async_wait(boost::bind(&printer::print, this));
}
// 在析構中打印結果
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
// 作為類的成員函數,無需再傳入參數,直接使用當前對象的成員變量
void print()
{
if (count_ < 5)
{
std::cout << count_ << std::endl;
++count_;
timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1));
timer_.async_wait(boost::bind(&printer::print, this));
}
}
private:
boost::asio::steady_timer timer_;
int count_;
};
int main()
{
// main 里的調用簡單了很多
boost::asio::io_context io;
printer p(io);
io.run();
return 0;
}
多線程中的回調函數同步
asio 庫提供了一種保證,即只能從當前調用 io_context::run() 的線程調用回調函數。因此,僅從一個線程調用 io_context::run() 回調函數無法並發運行
這里需要使用 io_context::strand 來控制分配 handler 的執行,它可以保證無論 io_context::run() 的線程有多少,同時只能有一個 handler 程序執行,保證了共享資源的線程安全
在本例中 handler (print1 / print2) 所訪問的共享資源包括 std::cout 和成員變量 count_
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
class printer
{
public:
printer(boost::asio::io_context& io)
: strand_(io), // strand 成員,用於控制 handler 的執行
timer1_(io, boost::asio::chrono::seconds(1)), // 運行兩個計時器
timer2_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
// 啟動異步操作時,每個 handler 都綁定到 strand 對象
// bind_executor() 返回一個新的 handler,它將自動調度其包含的 printer::print1
// 通過將 handler 綁定到同一個 strand,保證它不會同時執行
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
void print1()
{
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << std::endl;
++count_;
timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1));
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
}
}
void print2()
{
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << std::endl;
++count_;
timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
}
private:
boost::asio::io_context::strand strand_;
boost::asio::steady_timer timer1_;
boost::asio::steady_timer timer2_;
int count_;
};
int main()
{
boost::asio::io_context io;
printer p(io);
// main 函數現在有兩個線程調用 io_context::run(),主線程和由 boost::thread 對象執行的額外線程
boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
io.run();
t.join();
return 0;
}
參考: