zoukankan      html  css  js  c++  java
  • 【Boost】boost库中timer定时器 2

    博客转载自:http://blog.csdn.net/yockie/article/details/40386145

    先跟着boost文档中asio章节的指南中的几个例子学习一下使用:

    所有的Asio类只要简单的包含"asio.hpp"头文件便可使用:#include <boost/asio.hpp>

    因为本程序中使用了定时器,我们需要包含相应的的Boost.Date_Time 头文件来处理时间操作:

    使用Asio的所有程序都至少需要一个提供访问I/O功能的io_service 对象。因此在主函数中我们做的第一件事就是声明一个这个类型的对象:boost::asio::io_service io;

    接下来我们声明一个boost::asio::deadline_timer类型的对象。作为 Asio的核心类,它提供的I/O功能(在此为定时器功能)通常用一个io_service 的引用作为其构造函数的第一个参数。第二个参数设置一个从现在开始5秒后终止的定时器。

    boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

    可以看一下boost::asio::deadline_timer的几个构造函数:

    basic_deadline_timer(  
        boost::asio::io_service & io_service);  
      
    basic_deadline_timer(  
        boost::asio::io_service & io_service,  
        const time_type & expiry_time);  
      
    basic_deadline_timer(  
        boost::asio::io_service & io_service,  
        const duration_type & expiry_time);  

    注意后两种的区别,说明以下2种用法是等价的:

    boost::asio::deadline_timer t(io, boost::posix_time::microsec_clock::universal_time()+boost::posix_time::seconds(5));  
    boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  

    在这个简单的程序中,我们用定时器演示一个阻塞等待。

    #include <iostream>  
    #include <boost/asio.hpp>  
    #include <boost/date_time/posix_time/posix_time.hpp>  
      
    int main()  
    {  
      boost::asio::io_service io;  
      
      boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  
      t.wait();  
      
      std::cout << "Hello, world!
    ";  
      
      return 0;  
    } 

    deadline_timer::wait() 函数调用直到定时器终止(从定时器被创建算起,五秒后终止)才会返回。一个deadline timer 通常是下面两种状态中的一种:"expired(终止)" 或"not expired(不终止)"。
    如果deadline_timer::wait() 函数被一个已经终止的定时器调用, 它将立即返回。

    2. Timer.2 - 使用异步定时器

    本例使用Asio的异步回调功能在定时器中演示一个异步等待。

    使用Asio的异步功能意味着当一个异步操作完成时一个回调函数将被调用。在本程序中我们定义一个名为print 的函数,在异步等待结束后这个函数将被调用,

    最后我们打印出 "Hello, world!" 信息以显示定时器已经终止。

    void print(const boost::system::error_code& /*e*/)
    {
    std::cout << "Hello, world!
    ";
    }
    
    int main()
    {
    boost::asio::io_service io;
    
    boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

    接下来,我们调用 deadline_timer::async_wait() 函数执行一个异步等待去取代Timer.1例中的阻塞等待。当调用这个函数时我们传入上面定义的print回调句柄。

    t.async_wait(print);

    最后,我们必须在io_service对象上调用io_service::run()成员函数。

    Asio保证回调句柄仅仅能被io_service::run()启动的当前线程所调用。 因此,如果io_service::run() 函数不执行,用于异步等待完成时的回调函数将永远不会被调用。

    当仍旧有“工作”可做时,io_service::run() 函数会继续运行。在本例中,“工作”是定时器的异步等待,因此,直到定时器终止和回调函数执行完成,程序才会返回。

    在调用io_service::run()之前确保给 io_service 一些工作去做,这非常重要。 例如,如果我们省略了上面调用的deadline_timer::async_wait() 函数,io_service对象将没有任何事情去做,因此io_service::run() 将立即返回。

    和同步方式相比,它主要有两点不同:
    (1) 调用的是非阻塞函数async_wait,它的入参是一个回调函数。
    (2) 显式调用io_service.run()函数驱动异步IO调度。

    值得提出的是,异步回调函数handler的参数中有一个error,注意这个error很重要,表明这个handler是因为超时被执行还是因为被cancel。

    符合2种情况之一,handler被执行:超时或者被cancel(可以通过此error的值进行区分)。这同时隐含的说明了除非io.stop被调用,否则handler一定会被执行。即便是被cancel。
    被cancel有多种方法,直接调用cancel或者调用expires_at,expires_from_now重新设置超时时间。

    #include <iostream>    
    #include <boost/asio.hpp>  
    #include <boost/thread.hpp>  
    #include <boost/date_time/posix_time/posix_time.hpp>  
    using namespace std;    
       
      
    void Print(const boost::system::error_code &ec)  
    {  
        cout<<"Hello World!"<<endl;  
        cout<<boost::this_thread::get_id()<<endl;  
    }  
    int main()  
    {    
        cout<<boost::this_thread::get_id()<<endl;  
        boost::asio::io_service io;  
        boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  
      
        t.async_wait(Print);  
        cout<<"to run"<<endl;  
        io.run();  
        cout<<"exit"<<endl;  
        return 0;    
    }    
    结果:
    2f98
    to run
    
    (此处等了5s)
    Hello World!
    2f98  (说明是同一线程)
    exit

    3. 回调函数绑定参数

    本例使定时器每秒被激活一次。例子将示范如何给你的函数指针传递附加参数。

    使用Asio实现一个重复定时器,你必须在你的回调函数中去改变定时器的终止时间,然后开始一个新的异步等待。显然这意味着回调函数必须拥有改变定时器对象的权限。为此我们为 print函数增加两个新参数:

    (1) 一个指向定时器对象的指针。 
    (2) 一个用于当定时器第6次被激活时我们可以中止程序的计数器。 

    void print(const boost::system::error_code& /*e*/,boost::asio::deadline_timer* t, int* count)
    {
      if (*count < 5)   {     std::cout << *count << " ";     ++(*count)
      } }

    如上所示,示例程序使用了一个计数器,当定时器被第6次激活时,用来中止程序。然而,你将看到这里并没有显式地要求io_service对象中止。回忆示例2中,当没有更多“工作”去做时,io_service::run() 函数完成。在计数器达到 5时,定时器并没有启动一个新的异步等待。该io_service执行完工作后停止运行。

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));

    接着,我们推迟定时器的终止时间。通过在原先的终止时间上增加延时,我们可以确保定时器不会在处理回调函数所需时间内到期。 

    接着我们在定时器中启动一个新的异步等待。我们必须使用boost::bind() 函数给你的回调函数绑定额外的参数,因为deadline_timer::async_wait() 函数只期望得到一个拥用 void(const boost::system::error_code&) 签名的函数指针(或函数对象)。为你的print函数绑定附加的参数后,它就成为与签名精确匹配的函数对象。
    在本例中,boost::bind()的boost::asio::placeholders::error参数是为了给回调函数传入一个error对象。当开始异步操作时,如果使用boost::bind(),你必须指定和回调函数的参数列表相匹配的一个参数。在示例4中,如果在回调函数中,这个参数不是必需的,这个占位符会被省略。

    t->async_wait(boost::bind(print,boost::asio::placeholders::error, t, count));
    #include <iostream>    
    #include <boost/asio.hpp>  
    #include <boost/thread.hpp>  
    #include <boost/date_time/posix_time/posix_time.hpp>  
    using namespace std;    
       
      
    void Print(const boost::system::error_code &ec,  
                boost::asio::deadline_timer* pt,  
                int * pcount)  
    {  
        if (*pcount < 3)  
        {  
            cout<<"count = "<<*pcount<<endl;  
            cout<<boost::this_thread::get_id()<<endl;  
            (*pcount) ++;  
      
            pt->expires_at(pt->expires_at() + boost::posix_time::seconds(5)) ;  
      
            pt->async_wait(boost::bind(Print, boost::asio::placeholders::error, pt, pcount));  
              
        }  
    }  
    int main()  
    {    
        cout<<boost::this_thread::get_id()<<endl;  
        boost::asio::io_service io;  
        boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  
        int count = 0;  
        t.async_wait(boost::bind(Print, boost::asio::placeholders::error, &t, &count));  
        cout<<"to run"<<endl;  
        io.run();  
        cout << "Final count is " << count << "
    ";  
        cout<<"exit"<<endl;  
        return 0;    
    } 
    结果:
    14d0
    to run
    
    (等了5s)
    count = 0
    14d0
    
    (等了5s)
    count = 1
    14d0
    
    (等了5s)
    count = 2
    14d0
    
    (等了5s)
    Final count is 3
    exit

    4. 多线程同步回调 

    本例示范了使用boost::asio::strand 类来创建多线程程序中的同步回调句柄。 

    前几个例程只是在单线程下使用io_service::run() 函数来避免处理函同步。 如你所见,Asio库保证回调句柄仅能被当前正在调用 io_service::run(). 函数的线程调用。 因此,在单线程中调用io_service::run() 能确保回调句柄不被并发运行。

    下面是Asio在单线程程序中的局限性,尤其是服务器方面,包括: 

    (1)操作需要较长时间处理才能完成时弱响应。 
    (1)在大规模的多处理机系统中表现不佳。 


    如果你发现自己陷入这些局限时,一个可供选择的方法是创建一个每个线程都调用io_service::run() 的线程池。 不过,因为这允许并发操作,当访问一个共享、非线程安全的资源时,我们需要一个同步方式。

    让我们从定义一个名为printer的类开始,这与前一个示例中的类很相似。这个类是上一个例子的扩展,这里我们使用两个并行的定时器。

    class CPrinter

    除了初始化一对boost::asio::deadline_timer 成员变量外,构造函数还初始化一个boost::asio::strand类型strand_ 成员变量。
    boost::asio::strand 对象保证:对于通过它来分派执行的众操作中,只有一个操作执行完成之后才允许进入下一个操作。 这种保证与多少个线程调用io_service::run() 无关。当然,如果不是通过一个boost::asio::strand对象分派, 或者通过其它不同的boost::asio::strand对象分派,这些操作仍旧可能是并发的。

    CPrinter(boost::asio::io_service &io)
    :m_strand(io)
    ,m_timer1(io, boost::posix_time::seconds(5))
    ,m_timer2(io, boost::posix_time::seconds(5))
    ,m_count(0)
    {
    
    m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ));
    m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ));
    }
    
    ~CPrinter()
    {
    cout<<"m_count = "<<m_count<<endl;
    }

    当开始同步操作时,每一个回调句柄都使用boost::asio::strand对象进行“包装”。strand::wrap() 函数返回一个新的通过boost::asio::strand对象自动分派的内部句柄。 通过同一boost::asio::strand对象对句柄进行“ 包装”,我们可以保证操作不会并发执行。
    在一个多线程程序中,当访问同一共享资源时,异步操作必须是同步的。在本例中,print1 和print2)函数使用的共享资源std::cout 和count_数据成员。

    void Print1()
    {
    if (m_count < 10)
    {
    cout<<"Timer1 count = "<<m_count<<endl;
    cout<<boost::this_thread::get_id()<<endl;
    m_count++;
    
    m_timer1.expires_at(m_timer1.expires_at() + boost::posix_time::seconds(1)) ;
    
    m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );
    }
    }
    
    void Print2()
    {
    if (m_count < 10)
    {
    cout<<"Timer2 count = "<<m_count<<endl;
    cout<<boost::this_thread::get_id()<<endl;
    m_count++;
    
    m_timer2.expires_at(m_timer2.expires_at() + boost::posix_time::seconds(1)) ;
    
    m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );
    }
    }

    main函数中, io_service::run() 现在被两个线程调用:主线程和一个附加线程。这一切依赖于boost::thread对象来完成。 
    正如它被一个单线程调用一样,io_service::run()的并发调用会一直持续到无任何“工作”可做。后台线程直到所有异步操作都完成后才会退出。

    #include <iostream>    
    #include <boost/asio.hpp>  
    #include <boost/thread.hpp>  
    #include <boost/bind.hpp>  
    #include <boost/date_time/posix_time/posix_time.hpp>  
    using namespace std;    
       
      
      
    class CPrinter  
    {  
    public:  
        CPrinter(boost::asio::io_service &io)  
            :m_strand(io)  
            ,m_timer1(io, boost::posix_time::seconds(5))  
            ,m_timer2(io, boost::posix_time::seconds(5))  
            ,m_count(0)  
        {  
            m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );  
            m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );  
        }  
        ~CPrinter()  
        {  
            cout<<"m_count = "<<m_count<<endl;  
        }  
      
        void Print1()  
        {  
            if (m_count < 10)  
            {  
                cout<<"Timer1 count = "<<m_count<<endl;  
                cout<<boost::this_thread::get_id()<<endl;  
                m_count++;  
      
                m_timer1.expires_at(m_timer1.expires_at() + boost::posix_time::seconds(1)) ;  
      
                m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );  
            }  
        }  
      
        void Print2()  
        {  
            if (m_count < 10)  
            {  
                cout<<"Timer2 count = "<<m_count<<endl;  
                cout<<boost::this_thread::get_id()<<endl;  
                m_count++;  
      
                m_timer2.expires_at(m_timer2.expires_at() + boost::posix_time::seconds(1)) ;  
      
                m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );  
            }  
        }  
    private:  
        boost::asio::strand m_strand;  
        boost::asio::deadline_timer m_timer1;  
        boost::asio::deadline_timer m_timer2;  
        int m_count;  
      
    };  
      
      
    int main()  
    {    
        cout<<boost::this_thread::get_id()<<endl;  
        boost::asio::io_service io;  
        CPrinter cp(io);  
      
        cout<<"to run"<<endl;  
      
        boost::thread td(boost::bind(&boost::asio::io_service::run, &io));  
      
      
        io.run();  
      
        cout<<"exit"<<endl;  
        return 0;  

    运行结果

    620c
    to run
    Timer2 count = 0
    79c4
    Timer1 count = 1
    620c
    Timer1 count = 2
    620c
    Timer2 count = 3
    79c4
    Timer2 count = 4
    79c4
    Timer1 count = 5
    620c
    Timer1 count = 6
    620c
    Timer2 count = 7
    79c4
    Timer2 count = 8
    79c4
    Timer1 count = 9
    620c
    exit
    m_count = 10

    说明:

    (1)两个Timer确实是在不同线程中执行,并且只有一个print操作执行完成之后才允许进入另一个print操作

    (2)Timer1始终在一个线程中执行,Timer2始终在另一个线程中执行,(但不一定就是Timer1在主线程执行,这个分配时随机的)

    注意:

    deadline_timer和socket一样,都用io_service作为构造函数的参数。在其上进行异步操作,都将导致和io_service所包含的iocp相关联。这同样意味着在析构 io_service之前,必须析构关联在这个io_service上的deadline_timer

    引申:

    关于boost::asio::strand,有三个函数:post, dispatch, wrap

    post: 不管什么情况都会把任务丢到队列中,然后立即返回。如:m_strand.post(boost::bind(print, 2));
    dispatch: 如果跟run()在一个线程,那么任务会直接在dispatch内部调用,执行结束后返回。不在一个线程跟post一样。如:m_strand.dispatch(boost::bind(print, 1));

    wrap:返回一个新的通过boost::asio::strand对象自动分派的内部句柄

    参考:《boost技术文档》



  • 相关阅读:
    用于爬取知乎某个话题下的精华问题中所有回答的爬虫
    BSP -- 图书共享系统(Book Sharing Platform)
    【已解决】WPS2018 从第三页开始插入页眉页码(即前两页不要页眉页码)
    【编译原理】大白话讲解 First 集和 Follow 集的构造算法
    如果
    HTTP协议(1)------->资源和URL
    JavaWeb框架_Struts2_(八)----->Struts2的国际化
    深入理解java虚拟机----->垃圾收集器与内存分配策略(下)
    深入理解java虚拟机----->垃圾收集器与内存分配策略(上)
    JavaWeb框架_Struts2_(七)----->文件的上传和下载
  • 原文地址:https://www.cnblogs.com/flyinggod/p/8324711.html
Copyright © 2011-2022 走看看