asio 使用_future 而不是 yield[ec]

Posted

技术标签:

【中文标题】asio 使用_future 而不是 yield[ec]【英文标题】:asio use_future instead of yield[ec] 【发布时间】:2020-07-16 11:08:16 【问题描述】:

我想制作期货容器,每个未来都是任务的无效结果,因此我可以在容器上使用wait_for_any,每个任务都是我目前使用yield_context实现的协程,并且在这个协程中启动返回ec的函数和我使用 ec 分析结果的结果。然后调用另一个协程传递相同的 yield_context 。 我想知道如何制作这个设计。 如果我将使用 use_future ,我如何将错误代码传递给 ec 而不是抛出它,除非除了抛出它之外别无他法,在这种情况下,我将尝试并捕获异步启动函数。 所有这些任务都将在 asio io_service 上发布、生成……。 这是我的主要代码部分: 这是任务的产物

boost::asio::spawn(GetServiceReference(), boost::bind(&HTTPRequest::Execute, boost::placeholders::_1, m_HttpClient_request_name, Get_mHTTPClient_Responses_Map()));

这是使用 yield_context 的协程

void HTTPRequest::Execute(boost::asio::yield_context yield_r, std::string request_name, std::map<std::string, boost::shared_ptr<HTTPResponse>>& mHTTPClient_Responses_Map)

    resolver_iterator iterator_connect = boost::asio::async_connect(mSock, iterator_resolve, yield_r[ec]);

在Execute里面我们用ec来分析

if (ec == boost::system::errc::errc_t::success)

这里我们启动另一个协程传递相同的 yield_context

SendRequest(yield_r);

我想改变这一点,所以我有所有产生的 Execute 的期货容器,我不关心 Execute 的结果,因为我把它们放到成员类 Response 中。 但是我将来需要结果,以便我可以在容器上使用 wait_any 。

【问题讨论】:

【参考方案1】:

如果您可以更改您的实现,请使用 async_result 模式。

这样您就可以将您的方法与任何方法(完成处理程序、yield 上下文或use_future)一起使用。

我复制了here 中的独立示例以获得灵感:

综合演示

展示如何与 with 一起使用

coro 和产量[ec] coro 和产量 + 异常 std::future 完成处理程序

Live On Coliru

#define BOOST_COROUTINES_NO_DEPRECATION_WARNING 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/use_future.hpp>

using boost::system::error_code;
namespace asio = boost::asio;

template <typename Token>
auto async_meaning_of_life(bool success, Token&& token)

#if BOOST_VERSION >= 106600
    using result_type = typename asio::async_result<std::decay_t<Token>, void(error_code, int)>;
    typename result_type::completion_handler_type handler(std::forward<Token>(token));

    result_type result(handler);
#else
    typename asio::handler_type<Token, void(error_code, int)>::type
                 handler(std::forward<Token>(token));

    asio::async_result<decltype (handler)> result (handler);
#endif

    if (success)
        handler(error_code, 42);
    else
        handler(asio::error::operation_aborted, 0);

    return result.get ();


void using_yield_ec(asio::yield_context yield) 
    for (bool success :  true, false ) 
        boost::system::error_code ec;
        auto answer = async_meaning_of_life(success, yield[ec]);
        std::cout << __FUNCTION__ << ": Result: " << ec.message() << "\n";
        std::cout << __FUNCTION__ << ": Answer: " << answer << "\n";
    


void using_yield_catch(asio::yield_context yield) 
    for (bool success :  true, false ) 
    try 
        auto answer = async_meaning_of_life(success, yield);
        std::cout << __FUNCTION__ << ": Answer: " << answer << "\n";
     catch(boost::system::system_error const& e) 
        std::cout << __FUNCTION__ << ": Caught: " << e.code().message() << "\n";
    


void using_future() 
    for (bool success :  true, false ) 
    try 
        auto answer = async_meaning_of_life(success, asio::use_future);
        std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";
     catch(boost::system::system_error const& e) 
        std::cout << __FUNCTION__ << ": Caught: " << e.code().message() << "\n";
    


void using_handler() 
    for (bool success :  true, false )
        async_meaning_of_life(success, [](error_code ec, int answer) 
            std::cout << "using_handler: Result: " << ec.message() << "\n";
            std::cout << "using_handler: Answer: " << answer << "\n";
        );


int main() 
    asio::io_service svc;

    spawn(svc, using_yield_ec);
    spawn(svc, using_yield_catch);
    std::thread work([] 
            using_future();
            using_handler();
        );

    svc.run();
    work.join();

打印:

using_yield_ec: Result: Success
using_yield_ec: Answer: 42
using_yield_ec: Result: Operation canceled
using_yield_ec: Answer: 0
using_future: Answer: 42
using_yield_catch: Answer: 42
using_yield_catch: Caught: Operation canceled
using_future: Caught: Operation canceled
using_handler: Result: Success
using_handler: Answer: 42
using_handler: Result: Operation canceled
using_handler: Answer: 0

注意:为简单起见,我没有添加输出同步,因此输出可能会根据运行时执行顺序而混合

【讨论】:

我在搜索过程中研究了您的答案,并且它已经被加入书签,但我发现很难理解将完成令牌转换为处理程序时实际发生的情况。所有示例都谈到使用签名来形成处理程序类型。但形成的处理程序只是原型。处理程序没有主体。所以当异步操作完成时会调用什么????。关于我当前的问题,我仍然对操作的顺序感到困惑。例如:我将打包任务然后将它们添加到容器中。 ..但是我是从同一个线程还是不同线程启动 ios.run? 完成评论:我应该在哪里调用:wait_for_any???在我执行任务的同一个线程中或在 ios.run 之后“在相同或不同线程中”??? 现在我想我明白了处理程序的含义......它只是类型确定。并且有几种情况“我认为这些被称为类型特征”在编译时确定什么是类型处理程序传递,并据此他们将签名的参数传递给确定的类型“或部分参数,例如未来的情况”......所以如果它是函数对象,则将调用从用户传递的参数“lambda,bind, callback,function pointer" 或者在 use_future 的情况下它是未来的制造机制,或者它只是在有赌注协程的情况下切换上下文或者它是无赌注的 >>> 协程,以防 move(coro) 被传递。我是对的吗????现在我们如何使用这些不同的完成令牌与 spawn ,发布???我们可以避免构建 async_function 并使用 packaged_task将函数转换为返回未来的任务???生成的任务是否与 async_function 相同?我们将使用 async_function 和打包任务还是 using_future?? 你能发布一个新问题吗?一般来说,packaged_task 是不等价的,主要是因为它擦除了实际的处理程序类型。处理程序类型包括诸如何时使用链之类的信息。通过使用 packaged_task 擦除类型,ASIO 会丢失实际的处理程序类型并且无法维护语义。

以上是关于asio 使用_future 而不是 yield[ec]的主要内容,如果未能解决你的问题,请参考以下文章

boost::asio 与 boost::unique_future

使用 use_future 提升 Asio async_send_to 多播不起作用

在 io_service.stop() 之后等待 boost asio 的未来将永远持续下去

yield from 未与 future 一起使用

asyncio yield from concurrent.futures.Future of an Executor

__future__ 进口如何在幕后工作