asio如何生成和发布? [英] how spawn and post works with asio?

查看:71
本文介绍了asio如何生成和发布?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

// I asked this question
// https://stackoverflow.com/questions/61026135/asio-use-future-instead-of-yieldec
// and comments lead to need to modify code of answer and put in in this new
// question.
// I tried to ask questions in form  of code trials and causes of writing them
// or how i under stand them

// asio_packaged_task.cpp : Defines the entry point for the console application.

//#include "stdafx.h"
#define BOOST_COROUTINES_NO_DEPRECATION_WARNING
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/bind.hpp>
#include <iostream>

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); // 4-18-2020 this line happens when
                                   // async_meaning_of_life work is done,this
                                   // line is calling the handler and passing it
                                   // the result of  async_meaning_of_life
                                   // function which here for simplicity are
                                   // supplied as error_code{} and 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";
        }
}
// 4-18-2020 something interesting happens here,when we call the function
// using_future or using_handler in the same thread we get into these two
// functions then inside them we call async_meaning_of_life which is an
// initiating function ,the async_meaning_of_life has two parts: its code which
// ends before if(success) then it calls the completion token passed to it which
// is promise OR lambda "it might be fuction object ,functor,function pointer, "
// using handler(error,42) where handler represents the true handler type
// according to the token passed to function. then it returns the result by
// result.get to using_future or using_handler. inside using handler we notice
// that code returns back to lambda after handler(error,42) .if completion token
// were bind or function object,we would have seen code jumping to bound
// function or function object

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

void print(const boost::system::error_code& /*e*/) {
    std::cout << "Hello, world!" << std::endl;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Token>
auto async_meaning_of_life_composed(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

    // here i will add intermediate initiating functions

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

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

    // using_yield_ec(asio::yield_context yield);
    // spawn(svc, using_yield_ec);
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    if (success)
        handler(error_code{}, 42); // 4-18-2020 this line happens when
                                   // async_meaning_of_life work is done,this
                                   // line is calling the handler and passing it
                                   // the result of  async_meaning_of_life
                                   // function which here for simplicity are
                                   // supplied as error_code{} and 42
    else
        handler(asio::error::operation_aborted, 0);

    return result.get();
}

void using_future_composed() {
    for (bool success : { true, false })
        try {
            auto answer =
                async_meaning_of_life_composed(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";
        }
}

int main() {
    asio::io_service svc;

    boost::asio::steady_timer t(svc, boost::asio::chrono::seconds(45));
    // this function returns immediately and make new thread

    t.async_wait(&print);
    // this function returns immediately>>>>also it adds 1 out standing work to
    // svc.is async_wait body runned in main threaed OR in another thread????if
    // it is ran in another thread,how immediate return happens"not
    // blocking"??why async_meaning is not returning immediately like
    // async_wait?

    auto answer = async_meaning_of_life(true, asio::use_future);
    // this function does not return immediately and is executing in main thread
    // >>>>>how can we make it behave like async_wait???? first

    std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";
    svc.post([]() { // this adds 1 outstanding work to svc and does not start
        auto answer = async_meaning_of_life(true, asio::use_future);
        std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";
    });
    svc.post(using_future);

    // this increase outstanding work by 1

    // boost::asio::yield_context yield;
    // 4-18-2020 this is only used with spawn ,if you want to use stakeful
    // coroutines,use push and pull types of coroutine "i wonder how to do
    // this???"

    // using_yield_ec( yield);this is wrong usage
    // using_yield_catch( yield);this is wrong usage

    // using_future();this is normal usage but it does not return immediately
    // and it executes in main thread.
    // using_handler();
    svc.post(using_future_composed);
    spawn(svc, using_yield_ec);
    // this adds 2 outstanding work to svc why 2 works are made while we are
    // launching one function????

    spawn(svc, using_yield_catch);
    // what i think i understand about mechanism of work of spawn:spawn is
    // called from main thread>>>>it is just used with coroutines taking
    // yield_context as argument,spawn post function to service,spawn makes link
    // between the context in which service will be ran"may be main thread or
    // new thread AND the context of coroutine function ran in same thread as
    // service" or may be the coroutine makes new thread in which it is
    // running???".Then when svc.run is called,svc calls task"here svc is caller
    // and coroutine is callee",task is executing,yield is called as completion
    // token"can we call yield outside initiating function to switch to caller
    // "here caller is svc"????. then we are now in svc context which calls
    // another task .....

    // t.async_wait(&using_future);wrong usage leading to error?why can not in
    // use using_future function as completion callback with async_wait???

    // spawn(svc, using_future);wrong usage as using_future is not coroutine?

    std::thread work([] {
        using_future();
        using_handler();
        auto answer = async_meaning_of_life(true, asio::use_future);
        // this function does not return immediately and is executing in main
        // thread >>>>>how can we make it behave like async_wait???? first

        std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";

    });
    std::thread work_io([&] { // this starts new thread in which svc is run
        svc.run();
    });

    svc.run(); // this run svc in main thread

    // general question:
    /*
    using_* is considered normal function or coroutine OR composed operation??
    async_meaning is considered initiating function?

    why does not it return immediately when ran in main thread?how can we make
    it return immediately and then when certain result is present ,it calls its
    callback??

    async_wait is considered initiating function? why does it return
    immediately then when timer expires ,it calls back its completion token??

    can i make the following composed operation:

    i will make composed operation which returns future to caller thread,

    and inside it i shall call another composed operation with coroutine,
    */

    work.join();
    work_io.join();
}

推荐答案


boost::asio::steady_timer t(svc, boost::asio::chrono::seconds(45));
// this function returns immediately and make new thread

否,它不会创建新线程.它只是构造一个服务对象(计时器)并返回.很明显,就像构造字符串时返回的std::string s("hello");一样.

No it doesn't create a new thread. It just constructs a service object (the timer) and returns. obviously immediately, like std::string s("hello"); returns when the string is constructed.

t.async_wait(&print);
// this function returns immediately>>>>also it adds 1 out standing work to
// svc. is async_wait body runned in main threaed OR in another thread????if
// it is ran in another thread,how immediate return happens"not
// blocking"??why async_meaning is not returning immediately like
// async_wait?

放慢脚步.

async_wait主体是在主线程中运行还是在另一个线程中运行?

is async_wait body runned in main threaed OR in another thread?

这只是一个功能.它在当前线程上运行.就像您致电printf时一样.

It's just a function. It runs on the current thread. Like when you called printf.

如果它在另一个线程中运行,如何立即返回不是 阻止"?

if it is ran in another thread, how immediate return happens "not blocking"?

好吧,它不在另一个线程中.但是如果是这样,那么很明显 它将返回不阻止":因为工作未在 当前线程.

Well, it's not in another thread. But if it were, then it would be obvious how it would return "not blocking": because the work is not happening on the current thread.

为什么async_meaning_of_life没有像这样立即返回 async_wait?

Why async_meaning_of_life is not returning immediately like async_wait?

它将立即返回.

现在,有些微妙了:即使您将其与yield_context一起使用(在 协程).它将立即返回并导致协程屈服. 这意味着其他任务有机会在服务线程上运行,并且 仅当异步操作完成时,协程才会恢复.从 从协程的角度来看,它看起来像是在召唤 封锁.这是(堆栈式)协程的重点.它抽象化" 异步.

Now, a bit subtler: Even if you use it with a yield_context (inside a coroutine). It will return immediately and cause the the coroutine to yield. This means that other tasks get a chance to run on the service thread(s) and only when the async operation completed, the coroutine will be resumed. From the point of view of the coroutine, it will have appeared as if the call was blocking. This is the whole point of (stackful) coroutines. It "abstracts away" the asynchrony.

因此,是的,async_meaning_of_life始终(总是)立即(几乎)返回.

So, yes, async_meaning_of_life always (always) returns (almost) immediately.

svc.post([]() { // this adds 1 outstanding work to svc and does not start

正确.使用{poll|run}[_one,_for,_until]函数运行任务².

Correct. Use a {poll|run}[_one,_for,_until] function to run tasks².

    auto answer = async_meaning_of_life(true, asio::use_future);
    std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";

您在这里什么都没问,但是使用Future来立即等待它是 反模式¹.这绝对没用,因为它总是会产生阻塞 行为.

You don't ask anything here, but using a future just to await it immediately is an anti-pattern¹. It's absolutely useless, as it will always generate blocking behaviour.

您应该将未来存储在某个地方,进行其他工作,然后在您需要未来的结果(可能已经完成或可能尚未完成)时,等待它(.get()您应该将未来存储在某个地方,执行其他工作,然后当您需要将来的结果(可能尚未完成)时(例如,调用.get()).

You should store the future somewhere, do other work and then when you need the result of the future (and it may or may not have already been completed) you await it (.get() You should store the future somewhere, do other work and then when you need the result of the future (and it may or may not have already been completed) you await it (e.g. by invoking .get()).

// using_yield_ec( yield);this is wrong usage
// using_yield_catch( yield);this is wrong usage

正确.正确使用Asio服务将为您提供yield上下文.

Correct. Used correctly, the Asio service will provide a yield context for you.

// boost::asio::yield_context yield;
// 4-18-2020 this is only used with spawn ,if you want to use stakeful
// coroutines,use push and pull types of coroutine "i wonder how to do
// this???"

不知道.只需参考Boost Coroutine的文档(我建议使用Boost Coroutine2).对于Asio异步操作,这是没有主题的.

No idea. Just refer to the documentation of Boost Coroutine (I suggest Boost Coroutine2). This is off-topic for Asio async operations.

// using_future();this is normal usage but it does not return immediately
// and it executes in main thread.

嗯,嗯.您从一个最小的示例中获得了它,它仅显示了不同async_result令牌的机制.

Well, duh. You took it from a minimal example that ONLY shows the mechanics of the different async_result tokens.

只需参考以上几行:

您应该将未来存储在某个地方,进行其他工作,然后在需要时进行 未来的结果(可能已经完成,也可能尚未完成) 您等待它(.get()您应该将未来存储在某个地方,做其他工作 然后当您需要未来的结果时(它可能有也可能没有) 已经完成),您可以等待它(例如通过调用.get()).

You should store the future somewhere, do other work and then when you need the result of the future (and it may or may not have already been completed) you await it (.get() You should store the future somewhere, do other work and then when you need the result of the future (and it may or may not have already been completed) you await it (e.g. by invoking .get()).


svc.post(using_future_composed);

再次,我没有看到任何问题,但我认为这并不意味着您理解它.我踩.

Again, I see no questions, but I don't think it means you understand it. I tread.

我看到using_future_composed基本上是using_future,但调用了async_meaning_of_life_composed.

I see using_future_composed is basically using_future but calling async_meaning_of_life_composed instead.

现在看着async_meaning_of_life_composed,我不知道该怎么办.看起来像async_meaning_of_life,添加了随机的代码行,在应该仅调度异步操作的函数中执行了各种操作,包括阻塞操作(请参阅反模式¹).

Now looking at async_meaning_of_life_composed I have no idea what that's supposed to do. It looks like async_meaning_of_life with random lines of code added, doing all kinds of things including blocking operations (see anti-pattern¹) in a function that is supposed to schedule a async operation only.

那不是您想要做的.曾经.

That's just not what you want to do. Ever.

spawn(svc, using_yield_ec);
// this adds 2 outstanding work to svc why 2 works are made while we are
// launching one function????

老实说,我不知道.我认为这是因为coro本身的启动已发布到工作队列中,因此它可以从其中一个工作线程安全地例外运行.

Honestly, I do not know. I assume it's because the launch of the coro itself is posted onto the work queue, so it runs exception-safely from one of the worker threads.

这里最重要的是,您实际上还没有启动任何io-worker,请参见上面的[²].

The bigger point here is that you still haven't actually started any io-workers, see [²] above.

spawn(svc, using_yield_catch);
// what i think i understand about mechanism of work of spawn:spawn is
// called from main thread>>>>it is just used with coroutines taking
// yield_context as argument,spawn post function to service,spawn makes link
// between the context in which service will be ran"may be main thread or
// new thread AND the context of coroutine function ran in same thread as
// service"...

基本上,是的.

//          ... or may be the coroutine makes new thread in which it is
// running???" ...

绝对不是.协程和Asio都是在不需要多线程的情况下实现并发的设备/框架.协程将从不创建线程. Asio通常不会创建任何线程(除非在某些平台上实现某些类型的服务,但是它们将是实现细节,并且您的任务/处理程序将永远不会运行)这样的隐藏线程).

Definitely not. Both Coroutines and Asio are a device/framework to arrive at concurrency without necessarily multi-threading. Coroutine will never create a thread. Asio will typically not create any threads (unless to implement certain kinds of services on some platforms, but they'd be implementation-details and your tasks/handlers will never run on such a hidden thread).

//         ... .Then when svc.run is called,svc calls task"here svc is caller
// and coroutine is callee",task is executing,yield is called as completion
// token"can we call yield outside initiating function to switch to caller
// "here caller is svc"????. then we are now in svc context which calls
// another task .....

嗯.不,yield_context不是通往其他时空连续体的门户.

Huh. No, yield_context is not a portal to a different time-space continuum.

我不太确定调用收益"的含义,因此当您考虑从启动函数之外调用它时,我会说:可能不这样做.

I'm not very sure what you mean with `'call yield' so when you are thinking about calling it from outside the initiating function, I'd say: probably don't do that.

// t.async_wait(&using_future);wrong usage leading to error?why can not in
// use using_future function as completion callback with async_wait???

因为它不满足steady_time::async_wait的处理程序要求(仅应使用boost::system::error_code.您可能是指use_future(来自Asio)而不是您自己的using_future?

Because it doesn't satisfy the handler requirements for steady_time::async_wait (which should take a boost::system::error_code only. Did you perhaps mean use_future (from Asio) instead of your own using_future?

auto ignored_future = t.async_wait(boost::asio::use_future);

我承认名字有些混乱.如果有帮助,请将所有using_XYZ函数重命名为demonstration_using_XYZ.

I admit the names are somewhat confusing. If it helps, rename all the using_XYZ functions to demonstration_using_XYZ.

// spawn(svc, using_future);wrong usage as using_future is not coroutine?

您说对了.

std::thread work([] 
    using_future();
    using_handler();
    auto answer = async_meaning_of_life(true, asio::use_future);
    // this function does not return immediately and is executing in main
    // thread >>>>>how can we make it behave like async_wait???? first

    std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n";

});

我相信您只是复制/粘贴了评论,但万一您真的担心:不,这不在主线程上运行.它在work线程上运行,是的,这是因为您在future::get()上进行了阻塞.见上文¹.

I believe you just copy/pasted the comment, but in case you really worried: no that is not run on the main thread. It's run on the work thread, and yes, that's because you block on the future::get(). See above¹.

std::thread work_io([&] { // this starts new thread in which svc is run
    svc.run();
});

迟到总比没有好:)

svc.run(); // this run svc in main thread

正确无误,运行更多指令不会造成伤害.在多个线程上运行服务可能要求处理程序同步:

Correct, and running more doesn't hurt. Running the service on multiple threads may require handler synchronization: Why do I need strand per connection when using boost::asio?

// general question:
/*
using_* is considered normal function or coroutine OR composed operation??

正常功能(请参见上面将其重命名为demonstration_using_XYZ的说明)

Normal functions (see the clarification about renaming it to demonstration_using_XYZ above)

async_meaning is considered initiating function?

正确.

why does not it return immediately when ran in main thread? 

是的.看上面.如果您要说的话,为什么您自己的函数async_meaning_of_life_composed bblock?那是因为您使它执行了阻止操作(请参见上文).

It does. See above. If you mean, why does your own function async_meaning_of_life_composed bblock? That's because you made it do blocking operations (see above).

how can we make
it return immediately and then when certain result is present ,it calls its
callback??

通常的方法是启动其他异步操作.举例来说,您等待网络操作完成(例如,使用boost::asio::async_write异步操作),然后完成操作,然后调用handler. async_result帮助程序可以做到这一点,因此您不必了解实际的completion_handler_type,并且无论您如何调用启动函数,它都会神奇地"做正确的事情.

The usual way to do to it, is by launching other async operations. Say, for example, you wait for network operation to complete (asynchronously, e.g. using boost::asio::async_write) and when it's done, you invoke the handler. The async_result helper makes it so you don't have to know the actual completion_handler_type, and it will "magically" do the right thing regardless of how your initiating function was invoked.

async_wait is considered initiating function? why does it return
immediately then when timer expires ,it calls back its completion token??

因为这是异步操作的设计方式.之所以设计它们,是因为这样做很有用.

Because that's how async operations are designed. They were designed that way because that is useful behaviour.

can i make the following composed operation:

i will make composed operation which returns future to caller thread,

and inside it i shall call another composed operation with coroutine,
*/

您可以自由启动协程.只要确保您转移了async_result结果的所有权,就可以从那里调用处理程序,以表明操作已完成.

You are free to start a coroutine. Just make sure you transfer ownership of the async_result result so you can invoke the handler from there, to signal completion of your operation.

就期货而言,编写操作的通常方法是通过编写期货,例如:

In the case of futures, the usual way to compose operations is by composing futures, like: https://www.boost.org/doc/libs/1_72_0/doc/html/thread/synchronization.html#thread.synchronization.futures.then

std::string someotheroperation(int);

future<int> fut1 = foo();
future<std::string> fut2 = foo().then(someotheroperation);

奖金

(具有讽刺意味的是)Beast文档中的此页面是有关使用Asio编写组合操作"的最终文档.也许看到更多现实生活中的例子可能会给您带来更多想法.

BONUS

The ultimate piece of documentation on writing Composed Operations with Asio is (ironically) this page in the Beast documentation. Perhaps seeing some more real-life examples may give you more ideas.

请记住,Beast带有一些使/them/的库维护变得容易一些的功能,但是对于您自己的应用程序来说可能会显得过大.再说一次,如果您在您的道路上犯了错误,您将不会忽略重要的事情,例如我们讨论过的重要事情

Keep in mind Beast comes with a few facilities that make library maintenance for /them/ a bit easier, but could well be overkill for your own application. Then again, if you err on theur path you will not overlook important things like the one we discussed here earlier:

这篇关于asio如何生成和发布?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆