如何避免发射已经破坏的boost :: asio :: deadline_timer [英] How to avoid firing already destroyed boost::asio::deadline_timer

查看:125
本文介绍了如何避免发射已经破坏的boost :: asio :: deadline_timer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个io_service对象上使用了多个 boost :: asio :: deadline_timer std :: shared_ptr boost :: asio :: deadline_timer 存储在容器 std中:: map< int,std :: shared_ptr< debug_tim>>计时器,带有索引。

I'm using multiple boost::asio::deadline_timer on one io_service object. std::shared_ptr of boost::asio::deadline_timer are stored in the container std::map<int, std::shared_ptr<debug_tim>> timers with index.

在计时器处理程序中,我擦除了其他 boost :: asio :: deadline_timer 。但是,似乎擦除后的计时器经常会被触发成功错误代码。

In the timer handler, I erase other boost::asio::deadline_timer. However, it seems that the erased timer woule be often fired with success error code.

有什么办法可以避免这种情况。我希望对应于已擦除的 boost :: asio :: deadline_timer 的计时器处理程序始终在 Operation cancelled 的情况下触发。

Is there any way to avoid that. I expect that the timer handler that corresponding to the erased boost::asio::deadline_timer always fires with Operation canceled.

我想念一些东西吗?

这是再现行为的代码

https://wandbox.org/permlink/G0qzYcqauxdqw4i7

#include <iostream>
#include <memory>

#include <boost/asio.hpp>

// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
        std::cout << "debug_tim() " << i << std::endl;
    }
    ~debug_tim() {
        std::cout << "~debug_tim() " << i << std::endl;
    }
    int i;
};

int main() {
    boost::asio::io_service ios;
    std::map<int, std::shared_ptr<debug_tim>> timers;
    {
        for (int i = 0; i != 5; ++i) {
            auto tim = std::make_shared<debug_tim>(ios, i);
            std::cout << "set timer " << i << std::endl;
            tim->expires_from_now(boost::posix_time::seconds(1));
            timers.emplace(i, tim);
            tim->async_wait([&timers, i](auto ec){
                    std::cout << "timer fired " << i << " : " <<  ec.message() << std::endl;
                    auto it = timers.find(i);
                    if (it == timers.end()) {
                        std::cout << "  already destructed." << std::endl;
                    }
                    else {
                        int other_idx = i + 1; // erase other timer (e.g. i + 1)
                        timers.erase(other_idx);
                        std::cout << "  erased " << other_idx << std::endl;
                    }
                }
            );
        }
    }
    ios.run();
}

我也称 boost :: asio :: deadline_timer :: cancel(),然后再清除计时器。但是,我得到了类似的结果。这是取消版本:

I also call boost::asio::deadline_timer::cancel() before I erase the timer. However, I got similar result. Here is the cancel version:

https:// wandbox .org / permlink / uM0yMFufkyn9ipdG

#include <iostream>
#include <memory>

#include <boost/asio.hpp>

// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
        std::cout << "debug_tim() " << i << std::endl;
    }
    ~debug_tim() {
        std::cout << "~debug_tim() " << i << std::endl;
    }
    int i;
};

int main() {
    boost::asio::io_service ios;
    std::map<int, std::shared_ptr<debug_tim>> timers;
    {
        for (int i = 0; i != 5; ++i) {
            auto tim = std::make_shared<debug_tim>(ios, i);
            std::cout << "set timer " << i << std::endl;
            tim->expires_from_now(boost::posix_time::seconds(1));
            timers.emplace(i, tim);
            tim->async_wait([&timers, i](auto ec){
                    std::cout << "timer fired " << i << " : " <<  ec.message() << std::endl;
                    auto it = timers.find(i);
                    if (it == timers.end()) {
                        std::cout << "  already destructed." << std::endl;
                    }
                    else {
                        int other_idx = i + 1; // erase other timer (e.g. i + 1)
                        auto other_it = timers.find(other_idx);
                        if (other_it != timers.end()) {
                            other_it->second->cancel();
                            timers.erase(other_it);
                        }
                        std::cout << "  erased " << other_idx << std::endl;
                    }
                }
            );
        }
    }
    ios.run();
}

编辑

Felix,谢谢您的回答。我了解 boost :: asio :: deadline :: timer :: cancel()的行为。我始终需要注意 boost :: asio :: deadline :: timer 的生命周期。我是我项目的实际代码,``boost :: asio :: deadline :: timer`是另一个对象的成员变量,例如会话对象。并在计时器处理程序中访问对象。这很危险。

Felix, thank you for the answer. I understand the boost::asio::deadline::timer::cancel() behavior. I always need to care the lifetime of boost::asio::deadline::timer. I my actual code of my project, the ``boost::asio::deadline::timer` is a member variable of another object such as a session object. And in the timer handler, it accesses the object. It's dangerous.

我考虑如何编写安全代码。我想出了 std :: weak_ptr 来检查对象的生命周期。

I consider how to write safe code. And I come up with using std::weak_ptr in order to check the object's lifetime.

这里是更新的代码:

#include <iostream>
#include <memory>

#include <boost/asio.hpp>

// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
        std::cout << "debug_tim() " << i << std::endl;
    }
    ~debug_tim() {
        std::cout << "~debug_tim() " << i << std::endl;
    }
    int i;
};

int main() {
    boost::asio::io_service ios;
    std::map<int, std::shared_ptr<debug_tim>> timers;
    {
        for (int i = 0; i != 5; ++i) {
            auto tim = std::make_shared<debug_tim>(ios, i);
            std::cout << "set timer " << i << std::endl;
            tim->expires_from_now(boost::posix_time::seconds(1));
            timers.emplace(i, tim);

            // Capture tim as the weak_ptr wp
            tim->async_wait([&timers, i, wp = std::weak_ptr<debug_tim>(tim)](auto ec){
                    std::cout << "timer fired " << i << " : " <<  ec.message() << std::endl;

                    // Check the lifetime of wp
                    if (!wp.lock()) std::cout << "  timer freed." << std::endl; // return here on actual code

                    auto it = timers.find(i);
                    if (it == timers.end()) {
                        std::cout << "  already destructed." << std::endl;
                    }
                    else {
                        int other_idx = i + 1; // erase other timer (e.g. i + 1)
                        timers.erase(other_idx);
                        std::cout << "  erased " << other_idx << std::endl;
                    }
                }
            );
        }
    }
    ios.run();
}

这是避免访问具有<$的已删除对象的好方法c $ c> boost :: asio :: deadline_timer 吗?

Is this a good way to avoid accessing the deleted object that has the boost::asio::deadline_timer ?

编辑

我的weak_ptr解决方案效果很好。

My weak_ptr solution works well.

请参阅
如何避免发射已经破坏的boost :: asio :: deadline_timer

推荐答案

根据 reference date_timer :: cancel


如果在调用cancel()时计时器已经过期,则用于异步等待操作的处理程序将:

If the timer has already expired when cancel() is called, then the handlers for asynchronous wait operations will:


  • 已经被调用;或

  • have already been invoked; or

已在不久的将来排队等待调用。

have been queued for invocation in the near future.

这些处理程序将不再被取消,因此将传递一个错误代码,指示成功完成等待操作。

These handlers can no longer be cancelled, and therefore are passed an error code that indicates the successful completion of the wait operation.

我们知道调用 cancel()不能取消已经排队等待触发的计时器。

We can know that calling cancel() can not cancel the timer which has already been queued for firing.

似乎Dealine_timer没有覆盖析构函数。 (delighting_timer成员列表中没有析构函数)

And it seems that the dealine_timer doesn't override destructor. (There is no destructor in the member list of deadline_timer)

在您的代码段中,所有计时器几乎都在同一时间触发。关于asio将使用一些内部线程,很可能在调用一个完成处理程序时,其他的正在排队。

In your code snippet, all timers will fire at almost the same time. Concerning that asio will use some internal threads, it's quite probably that when one completion handler is called, the others are being queued.

这篇关于如何避免发射已经破坏的boost :: asio :: deadline_timer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆