Boost學習之asio庫的簡單使用

寫在前面
最近項目要實現flv視頻實時播放功能,需要提供http服務,供客戶端瀏覽器直播,因此涉及到C++開源庫Boost,其中的一個庫asio剛好可以滿足我的需求,因此學習並移植到自己的項目中,做筆記於此。

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 設定一些工作,如果省略了 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;
}

注:
原文鏈接

參考文章
Boost::asio
How strands work

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章