简体   繁体   English

boost :: asio :: deadline_timer没有醒来(压力情景)

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

I'm using a deadline_timer as an asynchronous event and I'm running into a situation where, after some time, the thread waiting on the event never seems to be woken up (despite more calls to cancel() ). 我正在使用deadline_timer作为异步事件,并且我遇到这样的情况,经过一段时间后,等待事件的线程似乎永远不会被唤醒(尽管有更多调用cancel() )。 I've been able to reproduce this using some sample code that I've pasted below; 我已经能够使用我在下面粘贴的一些示例代码重现这一点; it's not exactly consistent but I have seen what I think is the same issue I'm experiencing. 它并不完全一致,但我已经看到了我认为与我遇到的问题相同的问题。

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

Am I doing something here that's unsupported and inadvertently hitting some race condition? 我在这里做的事情是不受支持的,无意中遇到了一些竞争条件? The error code from the wait() never looks troublesome (even on the very last time it's woken up before it never seems to again). 来自wait()的错误代码从来看起来并不麻烦(即使是在最后一次它再次出现之前就已经被唤醒了)。 EDIT: I've also noticed the original bug on 3 different platforms (Windows, Mac and Linux) but the above test I've been using to reproduce has been on Windows. 编辑:我也注意到3个不同平台(Windows,Mac和Linux)上的原始错误,但我一直用于重现的上述测试已经在Windows上。

The deadline_timer object is not threadsafe. deadline_timer对象不是线程安全的。

You're canceling it from another thread than the one that's posting the async_wait. 你从另一个线程取消它而不是发布async_wait的线程。 This means the calls can race. 这意味着通话可以比赛。

I'm not sure how this can completely inhibit the callback, in your sample. 我不确定这样可以完全抑制样本中的回调。 It seems to me that the program should /just/ quit because the tight loop to 500000 finishes quickly (doing many redundant cancels that never get processed, because the coroutine would eg not even have posted the new async_wait). 在我看来,程序应该/只是/退出因为500000的紧密循环快速完成(执行许多永远不会被处理的冗余取消,因为协程甚至不会发布新的async_wait)。

So maybe you mean, "why don't I get 500000 events". 所以,也许你的意思是“为什么我不能获得500000个活动”。


UPDATE UPDATE

After the comment, here's a trivial transformation that shows how you are gonna be fine calling members on the timer from within an actor . 在评论之后,这是一个微不足道的转变,展示了如何在演员中调用计时器上的成员。 Note: this critically hinges on the idea that the io_service is run from a single thread only! 注意:这关键取决于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 counted\n";
}

Also, it looks like you just wanted to signal a background task. 此外,您似乎只想发出后台任务信号。 As given you can simplify the program like: 如您所述,您可以简化程序,如:

See it Live On Coliru 看到Live On 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 counted\n";
}

This does print all 500000 events: 打印所有500000个事件:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM