代码之家  ›  专栏  ›  技术社区  ›  Superlokkus

是否支持在同一个链上执行boost::asio::~strand<>?

  •  1
  • Superlokkus  · 技术社区  · 2 年前

    简短版本

    正在执行 boost::asio::~strand<> 在同一个 strand 支持?

    已找到文档

    文档似乎不一致,“遗留/不推荐使用” boost::asio::io_context::strand::~strand 文件

    通过链发布的尚未调用的处理程序仍将以满足非并发性保证的方式进行调度。

    虽然 boost::asio::strand::~strand 没有。在这方面,我认为这两条线索基本上是相同的,而且提到的句子意味着,我的假设得到了支持,但我无助于给出一个自信的答案。

    背景,即问题

    我在文档和演讲中使用boost asio propagtes shared_from_this 会话的生命周期自我管理,如tcp会话。因为除了那节课之外,没有人能主持 std::shared_ptr 就其本身而言,一个问题出现了:自然地,会话本身执行的代码最终会解构最后一个 shared_ptr ,触发其自身的析构函数 ~session . 这是在boost的例子中::asio无关,因为执行人有 boost::asio::context& 具有持久寿命或类似功能,并且代码可以使用隐式链。

    但假设会话需要一个明确的 boost::asio::strand ,因为我们需要一些并发的业务代码或自己的保持活动定时器,它被用作执行器。然后最后一个范围关闭 } 将执行的代码 ~session() ,将执行 ~strand() 同时在那个确切的链上执行。

    实例

    #include <iostream>
    #include <memory>
    
    #include <boost/asio.hpp>
    
    //More specific could be a tcp session for example
    struct session : std::enable_shared_from_this<session> {
        session (boost::asio::io_context& context) : strand(boost::asio::make_strand(context)), timer(context){}
        void start() {
            boost::asio::co_spawn(strand, loop(shared_from_this()), boost::asio::detached);
        }
    
        //Lets say is called from another co_routine, like a tcp eof
        void stop () {
            boost::asio::post(boost::asio::bind_executor(strand, [me = shared_from_this()] () {
                me->timer.cancel();
            }));
        }
    
    private:
        boost::asio::strand<boost::asio::io_context::executor_type> strand;
        boost::asio::steady_timer timer;
    
        boost::asio::awaitable<void> loop(std::shared_ptr<session> self) {
            while (true)  {
                timer.expires_from_now(std::chrono::seconds{5});
                co_await timer.async_wait(boost::asio::use_awaitable);
                std::cout << "5 seconds expired" << std::endl;
                self->stop();
            }
        } //Triggers ~session which, deconstructs strand, implications? UB?
    };
    
    
    int main(int argc, char* argv[]) {
        boost::asio::io_context context;
        {
            auto s = std::make_shared<session>(context);
            s->start();
        }
        while (true) {
            try {
                context.run();
                break;
            } catch (std::exception const& e) {
                std::cerr << "Exception in context::run(): " << e.what() << std::endl;
            }
        }
    }
    
    
    0 回复  |  直到 2 年前
        1
  •  1
  •   sehe    2 年前

    不,这不是问题。你不必担心的原因是“新股”是一个执行者。

    Executors 可廉价复制并按价值传递。任何保留它的东西都会有它的副本。你可以把执行人想象成一个把手。当然,实现仍然存在于执行上下文中注册的相应服务中。

    请注意,您在问题中比较的不同类型的线束 不同的 服务实现。坚持一种风格(当然是现代风格)会更有效率。

    Simpler+Explicit:执行器,而不是上下文引用

    我总是围绕执行者编写Asio代码:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <iostream>
    #include <memory>
    using namespace std::chrono_literals;
    namespace asio = boost::asio;
    
    using Executor = asio::any_io_executor;
    
    struct session : std::enable_shared_from_this<session> {
        session(Executor context) : timer(make_strand(context)) {}
    
        void start() {
            co_spawn(timer.get_executor(), loop(shared_from_this()), asio::detached);
        }
    
        void stop() {
            asio::post(timer.get_executor(), [me = shared_from_this()]() { me->timer.cancel(); });
        }
    
    private:
        asio::steady_timer timer;
    
        asio::awaitable<void> loop(std::shared_ptr<session> self) {
            while (true)  {
                timer.expires_from_now(5s);
                co_await timer.async_wait(asio::use_awaitable);
                std::cout << "5 seconds expired" << std::endl;
                self->stop();
            }
        }
    };
    
    int main() {
        asio::thread_pool context;
        {
            auto s = make_shared<session>(context.get_executor());
            s->start();
            std::this_thread::sleep_for(7s);
            s->stop();
        }
    
        context.join();
    }
    

    取消插槽?

    当然,现在没有什么值得共享所有权了,所以你可以等效地写它的功能:

    Live On Compiler Explorer

    #include <boost/asio.hpp>
    #include <iostream>
    #include <memory>
    using namespace std::chrono_literals;
    
    namespace asio = boost::asio;
    using Executor = asio::thread_pool::executor_type; // also optimize avoiding type-erasure and allocations
    using Action   = asio::awaitable<void, Executor>;
    
    Action do_session(asio::cancellation_slot sig) {
        auto token = asio::bind_cancellation_slot(sig, asio::deferred);
    
        asio::steady_timer timer{co_await asio::this_coro::executor};
    
        while (true) {
            timer.expires_from_now(5s);
            co_await timer.async_wait(token);
            std::cout << "5 seconds expired" << std::endl;
        }
    };
    
    int main() {
        asio::thread_pool context;
        {
            asio::cancellation_signal sig;
            co_spawn(context.get_executor(), do_session(sig.slot()), asio::detached);
    
            std::this_thread::sleep_for(7s);
            sig.emit(asio::cancellation_type::terminal);
        }
    
        context.join();
    }
    

    静止打印

    enter image description here