C++11 使用Boost.Asio,如果没有IO事件,是否可以添加一个处理程序,该处理程序在事件循环的每个迭代中执行一次?

C++11 使用Boost.Asio,如果没有IO事件,是否可以添加一个处理程序,该处理程序在事件循环的每个迭代中执行一次?,c++11,event-handling,boost-asio,C++11,Event Handling,Boost Asio,我目前正在使用Boost.asio1.68编写一个简单的服务器,我想知道是否有一种方法可以添加一个处理程序,在事件循环没有其他工作要做时执行 目前我有: void completionHandler (boost::asio::io_context* ioCtx){ // poll for some condition // if (condition) do some work; ioCtx->post(boost::bind(completionHandler, ioCtx

我目前正在使用Boost.asio1.68编写一个简单的服务器,我想知道是否有一种方法可以添加一个处理程序,在事件循环没有其他工作要做时执行

目前我有:

void completionHandler (boost::asio::io_context* ioCtx){
  // poll for some condition
  // if (condition) do some work;
  ioCtx->post(boost::bind(completionHandler, ioCtx));
}

//elsewhere
ioCtx->post(boost::bind(completionHandler, ioCtx));

但是,这与我想要做的并不完全匹配。

这不会达到您期望的效果

例如,使用单个
async\u accept
循环,您将永远不会达到“没有其他工作要做”的程度

同样,如果只有一方拥有未完成的
工作
(和),则永远不会出现“没有其他工作要做”的情况

基本上,您真正想要做的是链接轮询:

template <typename Condition, typename Handler, typename Executor>
void run_when(Executor& ex, Condition c, Handler h) {
    struct Op {
        Executor& _executor;
        Condition _cond;
        Handler _handler;

        Op(Executor& ex, Condition c, Handler h) 
            : _executor(ex), _cond(std::move(c)), _handler(std::move(h))
        { }

        void operator()() const {
            if (_cond())
                _handler(error_code{});
            else
                ba::post(_executor, std::move(*this));
        }
    };

    ba::post(ex, Op{ex, std::move(c), std::move(h)});
}
奖金 为什么我们的处理程序接受错误代码?嗯,与其他Asio操作一样,您可能希望能够取消它们。当(…)::Op实例时,要么让调用者负责延长
运行单元的生命周期,这会使生命变得复杂。或者,您可以使
条件
可调用项返回一个代码,指示该条件是否满足或等待是否已放弃\:

取决于
随机延迟()
的值



1(或者市长的女儿离婚了,因为错误代码非常通用)

警告:在现实生活中,你想限制你的回调链,因为如果没有其他东西排队,它将近似于一个繁忙的循环。
run_when(io,
        [&] { return bool(triggered); },
        [](error_code ec) { std::clog << "triggered: " << ec.message() << "\n"; });
#include <boost/asio.hpp>
namespace ba = boost::asio;
using boost::system::error_code;
using namespace std::chrono_literals;

template <typename Condition, typename Handler, typename Executor>
void run_when(Executor& ex, Condition c, Handler h) {
    struct RunWhen {
        Executor& _executor;
        Condition _cond;
        Handler _handler;

        RunWhen(Executor& ex, Condition c, Handler h) 
            : _executor(ex), _cond(std::move(c)), _handler(std::move(h))
        { }

        void operator()() const {
            if (_cond())
                _handler(error_code{});
            else
                ba::post(_executor, std::move(*this));
        }
    };

    ba::post(ex, RunWhen{ex, std::move(c), std::move(h)});
}

#include <iostream>
int main() {
    // some state that gets changed in the background
    std::atomic_bool triggered { false };
    std::thread([&] { 
            std::this_thread::sleep_for(1.5s);
            triggered = true;
        }).detach();

    ba::io_context io;

    // just some background polling that shall not block other work
    run_when(io, [&] { return bool(triggered); }, [](error_code ec) { std::clog << "triggered: " << ec.message() << "\n"; });

    io.run_for(3s);
}
triggered: Success
#include <boost/asio.hpp>
namespace ba = boost::asio;
using boost::system::error_code;
using boost::system::system_error;
using ba::error::operation_aborted;
using namespace std::chrono_literals;

template <typename Condition, typename Handler, typename Executor>
void run_when(Executor& ex, Condition c, Handler h) {
    struct Op {
        Executor& _executor;
        Condition _cond;
        Handler _handler;

        Op(Executor& ex, Condition c, Handler h) 
            : _executor(ex), _cond(std::move(c)), _handler(std::move(h))
        { }

        void operator()() const {
            try {
                if (_cond())
                    _handler(error_code{});
                else
                    ba::post(_executor, std::move(*this));
            } catch(system_error const& se) {
                _handler(se.code());
            }
        }
    };

    ba::post(ex, Op{ex, std::move(c), std::move(h)});
}

#include <random>
auto random_delay() {
    static std::mt19937 engine(std::random_device{}());
    return (std::uniform_int_distribution<>(1,2)(engine)) * 1s;
}

#include <iostream>
int main() {
    // some state that gets changed in the background
    std::atomic_bool triggered { false }, canceled { false };
    std::thread([&] { std::this_thread::sleep_for(1.5s); triggered = true; }).detach();

    // add a randomized cancellation
    {
        auto cancel_time = random_delay();
        std::clog << "hammer time: " << (cancel_time/1.0s) << "s\n";
        std::thread([&] { std::this_thread::sleep_for(cancel_time); canceled = true; }).detach();
    }

    ba::io_context io;

    // just some background polling that shall not block other work
    auto condition = [&] { return canceled? throw system_error(operation_aborted) : bool(triggered); };

    run_when(io, condition, [](error_code ec) { std::clog << "handler: " << ec.message() << "\n"; });

    io.run_for(3s);
}
hammer time: 1s
handler: Success
hammer time: 2s
handler: Success