boost :: asio :: deadline_timer :: async_wait不触发回调 [英] boost::asio::deadline_timer::async_wait not firing callback

查看:2630
本文介绍了boost :: asio :: deadline_timer :: async_wait不触发回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我使用每个客户端的计时器保持 unordered_map< string,shared_ptr< deadline_timer>< / code&但是,在设置 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
{
//取消当前等待操作(应该使用错误代码触发回调)
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。 p>

解决方案

您的代码/ looks / okay-ish。



我不知道你是如何得出结论,你的完成处理程序不会[...]发生(有错误代码)我重置指针。你忽略了这种情况(在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



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



如果你不希望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<<(取消< <客户端ID<定时器)<< 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>工作;
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__<< (<<<<<<<,<<< 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));
}
}


I have a boost io_service running in a thread, and I would like to fire a callback in that thread 6 seconds after a certain event happens to a client, and reset the timer for that client if it is already running.

I maintain a unordered_map<string, shared_ptr<deadline_timer>> with a timer for each client.

However, upon setting async_wait, my callback does not fire after the alloted amount of time (the io_service IS running), neither does it fire (with an error code) when I reset the pointer (which should call the destructor for the existing timer, causing it to post to the service). How can I fix this?

This is the relevant part of my code:

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);
    }
});

If it changes anything, I'm running under Visual C++ 2010 and boost 1.47.0.

解决方案

Your code /looks/ okay-ish.

I'm not sure how you are reaching the conclusion that your completion handler doesn't "[...] fire (with an error code) when I reset the pointer". You are ignoring this case (there is no else branch in the lambda).

How about writing the logic more clearly?

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);
    });
}

Here's a full demo with that else branch to let you see what is going on. I assumed 1 service thread.

See it Live On Coliru.

The test load is 100 session activities on 16 accounts in ~0.5s. The total running time is ~1.5s because I have reduced the session expiration from 6s to 1s for Coliru.

If you didn't want the destructor of LogonManager to wait for all sessions to expire, then clear the session table before joining the background thread:

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

Full Listing

#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));
    }
}

这篇关于boost :: asio :: deadline_timer :: async_wait不触发回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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