boost::asio::d eadline_timer 不会唤醒(压力场景)

boost::asio::deadline_timer doesn't wake up (stress scenario)

本文关键字:唤醒 压力 asio eadline timer boost      更新时间:2023-10-16

我正在使用deadline_timer作为异步事件,并且我遇到了这样一种情况:一段时间后,等待该事件的线程似乎永远不会被唤醒(尽管对cancel()的调用更多)。我已经能够使用粘贴在下面的一些示例代码来重现这一点;这并不完全一致,但我看到了我认为与我正在经历的问题相同的问题。

boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
int num_events = 0;
auto waiter = [&timer, &num_events](boost::asio::yield_context context) {
  while (true) {
    std::cout << "waiting on event" << std::endl;
    boost::system::error_code e;
    timer.async_wait(context[e]);
    std::cout << "got event (" << e << ")" << std::endl;
    ++num_events;
  }
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 500000; ++i) {
  timer.cancel();
  std::cout << i << std::endl;
}

我是不是在这里做了一些没有支撑的事情,无意中达到了某种比赛条件?来自wait()的错误代码看起来从来都不麻烦(即使是在它最后一次被唤醒时,它似乎再也不会被唤醒)。编辑:我也注意到了三个不同平台(Windows、Mac和Linux)上的原始错误,但我用来复制的上述测试是在Windows上进行的。

deadline_timer对象不是线程安全的。

您正在从发布async_wait的线程之外的另一个线程取消它。这意味着呼叫可能会竞争。

在您的示例中,我不确定这是如何完全抑制回调的。在我看来,程序应该/just/quit,因为到500000的紧密循环很快就完成了(做了许多从未处理过的冗余取消,因为协程甚至没有发布新的async_wait)。

所以,也许你的意思是,"为什么我不举办50万场活动"。


更新

在评论之后,这里有一个琐碎的转换,它显示了如何从actor内部调用计时器上的成员。注意:这主要取决于io_service仅从单个线程运行的想法!

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
    boost::asio::io_service     io_service;
    boost::asio::deadline_timer timer(io_service);
    timer.expires_at(boost::posix_time::pos_infin);
    boost::atomic_bool shutdown(false);
    int num_events = 0;
    auto waiter = [&timer, &num_events, &shutdown](boost::asio::yield_context context) {
        while (!shutdown) {
            std::cout << "waiting on event" << std::endl;
            boost::system::error_code e;
            timer.async_wait(context[e]);
            std::cout << "got event (" << e.message() << ")" << std::endl;
            ++num_events;
        }
    };
    boost::asio::spawn(io_service, std::move(waiter));
    boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
    for (auto i = 0; i < 5000; ++i) {
        io_service.post([&timer, i]{ 
                std::cout << i << std::endl;
                timer.cancel(); 
            });
    }
    io_service.post([&]{ 
            shutdown = true;
            timer.cancel();
        });
    thread.join();
    std::cout << "Check: " << num_events << " events countedn";
}

此外,看起来你只是想发出一个后台任务的信号。如给定,您可以简化程序,如:

查看Coliru直播

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
    io_service svc;
    int num_events = 0;
    auto work = boost::make_shared<io_service::work>(svc); // keep svc running
    boost::thread thread(boost::bind(&io_service::run, &svc));
    for (auto i = 0; i < 500000; ++i) {
        svc.post([&num_events,i]{
                std::cout << "got event (" << i << ")" << std::endl;
                ++num_events;
                });
    }
    work.reset();
    thread.join();
    std::cout << "Check: " << num_events << " events countedn";
}

将打印所有500000个事件:

got event (0)
got event (1)
got event (3)
...
got event (499998)
got event (499999)
Check: 500000 events counted