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

时间:2014-09-18 22:47:40

标签: c++ boost boost-asio

我正在使用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()的错误代码看起来从来都不会很麻烦(即使是在它最后一次醒来之前它似乎再也没有出现过)。编辑:我也注意到3个不同平台(Windows,Mac和Linux)上的原始错误,但上面用于重现的测试已在Windows上进行。

1 个答案:

答案 0 :(得分:2)

deadline_timer对象不是线程安全的。

你要从另一个帖子取消它而不是发布async_wait的帖子。这意味着通话可以比赛。

我不确定这样可以完全抑制样本中的回调。在我看来,该程序应该/只是/退出,因为500000的紧密循环很快完成(执行许多永远不会被处理的冗余取消,因为例程甚至没有发布新的async_wait)。

所以也许你的意思是,“为什么我不能获得500000个活动”。


更新

评论之后,这是一个微不足道的转变,显示你如何在演员中调用计时器上的成员。注意:这关键取决于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";
}

此外,您似乎只想发出后台任务信号。如上所述,您可以简化程序,如:

查看 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";
}

打印所有500000个事件:

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