boost :: asio :: deadline_timer :: async_wait不会触发回调

时间:2014-05-02 18:42:31

标签: c++ boost timer boost-asio

我在一个线程中运行了一个boost io_service,我想在客户端发生某个事件后6秒内在该线程中触发一个回调,并重置该客户端的计时器(如果它已经是运行。

我为每个客户端维护一个带有计时器的unordered_map<string, shared_ptr<deadline_timer>>

但是,在设置async_wait后,我的回调在分配的时间(io_service IS正在运行)之后不会触发,当我重置指针时它也不会触发(带有错误代码) (应调用现有计时器的析构函数,使其发布到服务)。我该如何解决这个问题?

这是我的代码的相关部分:

auto it = timersByClientId.find(clientId);
if (it == timersByClientId.end())
{
    onLogonChangeCallback(clientId, true);
    timersByClientId[clientId].reset(
        new boost::asio::deadline_timer(replyService, boost::posix_time::seconds(6))
    );
    it = timersByClientId.find(clientId);
}
else
{
    // Cancel current wait operation (should fire the callback with an error code)
    it->second.reset(
        new boost::asio::deadline_timer(replyService, boost::posix_time::seconds(6))
    );
}
it->second->async_wait([this, clientId](const boost::system::error_code& err) {
    if (!err)
    {
        onLogonChangeCallback(clientId, false);
    }
});

如果它发生任何变化,我将在Visual C ++ 2010下运行并提升1.47.0。

1 个答案:

答案 0 :(得分:3)

你的代码/看起来/好吧。

当我重置指针时,我不确定你是如何得出你的完成处理程序没有&#34; [...]触发(带有错误代码)的结论34; 。你忽略了这种情况(lambda中没有else分支。)

如何更清楚地编写逻辑?

void foo(int clientId) {
    shared_timer& timer = timersByClientId[clientId];

    if (!timer) 
        onLogonChangeCallback(clientId, true);

    timer = make_timer(); // reset

    timer->async_wait([this, clientId](const boost::system::error_code& err) {
        if (!err)
            onLogonChangeCallback(clientId, false);
    });
}

这是一个包含该else分支的完整演示,可让您了解正在发生的事情。我假设1个服务线程。

查看 Live On Coliru

测试负载是16个帐户的100个会话活动,大约0.5秒。总运行时间约为1.5秒,因为我已将Coliru的会话到期时间从6s减少到1s。

如果您不希望LogonManager的析构函数等待所有会话到期,那么在加入后台线程之前清除会话表:

~LogonMonitor() {
    work = boost::none;
    timersByClientId.clear();
    background.join();
}

完整列表

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/optional.hpp>
#include <boost/make_shared.hpp>

struct LogonMonitor {
    LogonMonitor() 
        : work(io_service::work(replyService)), background([this]{ replyService.run(); })
    { }

    ~LogonMonitor() {
        work = boost::none;
        // timersByClientId.clear();
        background.join();
    }

    void foo(int clientId) {
        shared_timer& timer = timersByClientId[clientId];

        if (!timer) 
            onLogonChangeCallback(clientId, true);

        timer = make_timer(); // reset

        timer->async_wait([this, clientId](const boost::system::error_code& err) {
            if (!err)
                onLogonChangeCallback(clientId, false);
            else
                std::cout << "(cancel " << clientId << " timer)" << std::endl;
        });
    }

  private:
    using io_service   = boost::asio::io_service;
    using timer        = boost::asio::deadline_timer;
    using shared_timer = boost::shared_ptr<timer>;

    io_service replyService;
    boost::optional<io_service::work> work;
    boost::thread background;

    std::map<int, shared_timer> timersByClientId;

    shared_timer make_timer() { 
        return boost::make_shared<timer>(replyService, boost::posix_time::seconds(/*6*/1)); 
    }

    void onLogonChangeCallback(int clientId, bool newLogon) 
    {
        std::cout << __FUNCTION__ << "(" << clientId << ", " << newLogon << ")" << std::endl;
    }
};

int main()
{
    LogonMonitor instance;
    for (int i = 0; i < 100; ++i)
    {
        instance.foo(rand() % 16);
        boost::this_thread::sleep_for(boost::chrono::milliseconds(rand() % 10));
    }
}