代码之家  ›  专栏  ›  技术社区  ›  zxctatar

如何在一个类中运行两个异步定时器

  •  2
  • zxctatar  · 技术社区  · 1 年前

    我想让第二个计时器在第二个开始工作半秒后开始工作,但当第二个定时器开始工作时,终端显示的内容与我想要的完全不同。如果它在执行第二个定时器之前没有施加限制,那么它们都可以正常工作

    #include <iostream>
    #include <functional>
    #include <boost/asio.hpp>
    
    class print{
    public:
        print(boost::asio::io_context& io) : timer_(io, boost::asio::chrono::seconds(1)), count_(0){
            timer_.async_wait(std::bind(&print::printer, this));
        }
    
        void printer(){
            if(count_ < 5){
                std::cout << count_ << "\n";
                ++count_;
    
                timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1));
                timer_.async_wait(std::bind(&print::printer, this));
            }
        }
    
        ~print(){
            std::cout << "Last num " << count_ << "\n";
        }
    private:
        boost::asio::steady_timer timer_;
        int count_;
    };
    
    int main(){
        boost::asio::io_context io;
        
        print p(io);
    
        boost::asio::steady_timer t(io, boost::asio::chrono::seconds(2));
        t.async_wait([&io](const boost::system::error_code& ec){
            if(!ec){
                print pp(io);
            }
        });
    
        io.run();   
    }
    

    这就是终端输出的

    0,
    1,
    Last num 0,
    2,
    3,
    4,
    Last num 5.
    
    1 回复  |  直到 1 年前
        1
  •  2
  •   sehe    1 年前

    第二个定时器 pp 是一个局部变量。它会立即被销毁。

    因此,异步操作使用一个已删除的实例并调用 Undefined Behaviour .

    您可能应该通过实际接收来检测取消 error_code 在完成处理程序中。这个 std::bind 表情默默地为你放下,现在。

    你可以使用某种动态分配。我还强烈建议将间隔计时器的概念和执行的操作分开并推广:

    struct Timer {
        using Callback = std::function<bool()>;
    
        Timer(asio::any_io_executor ex, duration d, Callback cb)
            : timer_(ex, d)
            , interval_(d)
            , cb_(std::move(cb)) { do_loop(); }
    
      private:
        asio::steady_timer timer_;
        duration           interval_;
        Callback           cb_;
    
        void do_loop() {
            timer_.async_wait(bind(&Timer::on_tick, this, std::placeholders::_1));
        }
    
        void on_tick(error_code ec) {
            if (ec) {
                std::cerr << "Warning: " << ec.message() << std::endl;
                return;
            } else if (cb_()) {
                timer_.expires_at(timer_.expiry() + interval_);
                do_loop();
            }
        }
    };
    

    现在您可以像这样使用它:

    #include <boost/asio.hpp>
    #include <functional>
    #include <iostream>
    namespace asio = boost::asio;
    using namespace std::chrono_literals;
    using boost::system::error_code;
    using duration = std::chrono::steady_clock::duration;
    
    struct Timer {
        using Callback = std::function<bool()>;
    
        Timer(asio::any_io_executor ex, duration d, Callback cb)
            : timer_(ex, d)
            , interval_(d)
            , cb_(std::move(cb)) { do_loop(); }
    
      private:
        asio::steady_timer timer_;
        duration           interval_;
        Callback           cb_;
    
        void do_loop() {
            timer_.async_wait(bind(&Timer::on_tick, this, std::placeholders::_1));
        }
    
        void on_tick(error_code ec) {
            if (ec) {
                std::cerr << "Warning: " << ec.message() << std::endl;
                return;
            } else if (cb_()) {
                timer_.expires_at(timer_.expiry() + interval_);
                do_loop();
            }
        }
    };
    
    int main() {
        asio::io_context io;
        auto ex = io.get_executor();
    
        auto printer = [](std::string_view name) {
            return [name, count_ = 0]() mutable {
                bool more = count_ < 5;
                std::cout << "Printer " << quoted(name) << (more ? " " : " Last num ") << count_ << "\n";
                ++count_;
                return more;
            };
        };
    
        std::optional<Timer> second;
    
        Timer first(ex, 1s, printer("First"));
        Timer delay(ex, 1500ms, [&] {
            second.emplace(ex, 1s, printer("Second"));
            return false;
        });
    
        io.run();
    }
    

    看看吧 Live On Coliru

    打印内容:

    Printer "First" 0
    Printer "First" 1
    Printer "Second" 0
    Printer "First" 2
    Printer "Second" 1
    Printer "First" 3
    Printer "Second" 2
    Printer "First" 4
    Printer "Second" 3
    Printer "First" Last num 5
    Printer "Second" 4
    Printer "Second" Last num 5
    

    或者更具互动性:

    enter image description here