我在一个线程中运行了一个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。
答案 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));
}
}