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技术文档》



  • 相关阅读:
    dubbo入门(一)
    java中文件操作《一》
    Unity 游戏框架搭建 2019 (七) 自定义快捷键
    凉鞋:我所理解的框架 【Unity 游戏框架搭建】
    Unity 游戏框架搭建 2019 (六) MenuItem 复用
    Unity 游戏框架搭建 2019 (五) 打开所在文件夹
    Unity 游戏框架搭建 2019 (四) 导出 UnityPackage
    Unity 游戏框架搭建 2019 (三) 生成文件名到剪切板
    Unity 游戏框架搭建 2019 (二) 文本复制到剪切板
    Unity 游戏框架搭建 2019 (一) 简介与第一个示例文件名的生成
  • 原文地址:https://www.cnblogs.com/flyinggod/p/8324711.html
Copyright © 2011-2022 走看看