boost asio example


Code Example

#include <iostream>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

/*
https://mmoaay.gitbooks.io/boost-asio-cpp-network-programming-chinese/content/Chapter3.html
https://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/tutorial/tuttimer2/src.html
sync: blocking ,return until job done
async: non-blocking, return immediately
*/

// Timer.1. - Using a timer synchronously
int snyc_timer()
{
    boost::asio::io_service  io;

    boost::asio::deadline_timer t(io, boost::posix_time::seconds(2));
    t.wait(); //  blocking wait on the timer

    std::cout << "Hello, world!" << std::endl;

    return 0;
}

// Timer.2 - Using a timer asynchronously
void print2(const boost::system::error_code& /*e*/)
{
    std::cout << "thread #" << boost::this_thread::get_id() << std::endl;
    std::cout << "Hello, world!" << std::endl;
}

int asnyc_timer()
{
    boost::asio::io_service io;

    boost::asio::deadline_timer t(io, boost::posix_time::seconds(2));
    t.async_wait(&print2); // asnyc,non-blocking,return immediately
    /*
    The asio library provides a guarantee that callback handlers will only be called from threads that are currently calling io_service::run(). 
    asio库会确保handler会在io_service::run()所在的thread中运行。
    */

    std::cout << "[main thread] #" << boost::this_thread::get_id() << std::endl;
    std::cout << "here" << std::endl;

    io.run();// sync: blocking ,return until job done

    return 0;
}

// Timer.3 - Binding arguments to a handler
void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{
    if (*count < 5)
    {
        std::cout << *count << std::endl;
        ++(*count);

        t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
        t->async_wait(boost::bind(print,
            boost::asio::placeholders::error, t, count));
    }
}

int asnyc_timer_with_params()
{
    boost::asio::io_service io;

    int count = 0;
    boost::asio::deadline_timer t(io, boost::posix_time::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;
}

// Timer.4 - Using a member function as a handler
class printer
{
public:
    printer(boost::asio::io_service& io)
        : timer_(io, boost::posix_time::seconds(1)),
        count_(0)
    {
        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_.expires_at() + boost::posix_time::seconds(1));
            timer_.async_wait(boost::bind(&printer::print, this));
        }
        else {
            std::cout << "print do nothing..." << std::endl;
        }
    }

private:
    boost::asio::deadline_timer timer_;
    int count_;
};

int asnyc_timer_with_class_method()
{
    boost::asio::io_service io;
    printer p(io);
    io.run();

    return 0;
}

// Timer.5 - Synchronising handlers in multithreaded programs

/*
The previous four tutorials avoided the issue of handler synchronisation 
by calling the io_service::run() function from one thread only. As you 
already know, the asio library provides a guarantee that callback handlers
will only be called from threads that are currently calling io_service::run(). 

Consequently, calling io_service::run() from only one thread ensures that 
callback handlers cannot run concurrently.

By wrapping the handlers using the same boost::asio::strand, we are ensuring
that they cannot execute concurrently.

在一个thread中调用io_service::run(),能够确保其对应的回调handlers不会并行执行。
但是在A和B两个thread中调用io_service::run(),A对应的handler和B对应的handler会并行执行。
【handler不是线程安全的】

如何解决多线程调用io_service::run() 其对应的handler会并行执行的问题?
使用boost::asio::io_service::strand对象。

An boost::asio::strand guarantees that, for those handlers that are dispatched 
through it, an executing handler will be allowed to complete before the next one
is started.

By wrapping the handlers using the same boost::asio::strand, we are ensuring that
they cannot execute concurrently.

同一个srand对象,能够确保其wrap的handler能够顺序执行。
即print1和print2在2个线程中不会并行执行。
*/
class printer_sync
{
public:
    printer_sync(boost::asio::io_service& io)
        : strand_(io),
        timer1_(io, boost::posix_time::seconds(1)),
        timer2_(io, boost::posix_time::seconds(1)),
        count_(0)
    {
        timer1_.async_wait(strand_.wrap(boost::bind(&printer_sync::print1, this)));
        timer2_.async_wait(strand_.wrap(boost::bind(&printer_sync::print2, this)));
    }

    ~printer_sync()
    {
        std::cout << "Final count is " << count_ << std::endl;
    }

    void print1()
    {
        if (count_ < 10)
        {
            std::cout << "Timer 1: " << count_ <<",thread #" << boost::this_thread::get_id() << std::endl;
            ++count_;

            timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
            timer1_.async_wait(strand_.wrap(boost::bind(&printer_sync::print1, this)));
        }
        else {
            std::cout << "Timer 1:  else " << count_ << std::endl;
        }
    }

    void print2()
    {
        if (count_ < 10)
        {

            std::cout << "Timer 2: " << count_ << ",thread #" << boost::this_thread::get_id() << std::endl;
            ++count_;

            timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
            timer2_.async_wait(strand_.wrap(boost::bind(&printer_sync::print2, this)));
        }
        else {
            std::cout << "Timer 2:  else " << count_ << std::endl;
        }
    }

private:
    boost::asio::io_service::strand strand_; // wrap handlers
    boost::asio::deadline_timer timer1_;
    boost::asio::deadline_timer timer2_;
    int count_;
};

int sync_handlers()
{
    boost::asio::io_service io;
    printer_sync p(io);
    boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
    std::cout << "[main thread] #" << boost::this_thread::get_id() << std::endl;

    std::cout << "[1] begin to run" << std::endl;
    io.run(); // blocking until job done
    std::cout << "[2] after run" << std::endl;
    t.join();
    std::cout << "[3] after join" << std::endl;
    return 0;
}

int main(int argc, char* argv[])
{
    //snyc_timer();
    //asnyc_timer();
    //asnyc_timer_with_params();
    //asnyc_timer_with_class_method();
    sync_handlers();
    return 0;
}

Reference

History

  • 20180523: created.

Author: kezunlin
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source kezunlin !
评论
  TOC