C++ 需要对同步与异步asio操作进行澄清

C++ 需要对同步与异步asio操作进行澄清,c++,asynchronous,tcp,boost-asio,synchronous,C++,Asynchronous,Tcp,Boost Asio,Synchronous,据我所知,同步和异步操作之间的主要区别。也就是说,write()或read()与async\u write()和async\u read()相比,前者在操作完成或出错之前不返回,而最后一个则立即返回 由于异步操作由一个io_service.run()控制,该服务在受控操作完成之前不会完成。在我看来,在顺序操作中,如与协议(如POP3)的TCP/IP连接中涉及的操作,其中操作是一个顺序,如: C: <connect> S: Ok. C: User... S: Ok. C: P

据我所知,同步和异步操作之间的主要区别。也就是说,
write()
read()
async\u write()
async\u read()
相比,前者在操作完成或出错之前不返回,而最后一个则立即返回

由于异步操作由一个
io_service.run()
控制,该服务在受控操作完成之前不会完成。在我看来,在顺序操作中,如与协议(如POP3)的TCP/IP连接中涉及的操作,其中操作是一个顺序,如:

 C: <connect>
 S: Ok.
 C: User...
 S: Ok.
 C: Password
 S: Ok.
 C: Command
 S: answer
 C: Command
 S: answer
 ...
 C: bye
 S: <close>
C:
S:好的。
C:用户。。。
S:好的。
C:密码
S:好的。
C:命令
S:回答
C:命令
S:回答
...
C:再见
S:
同步/异步运算符之间的差异没有多大意义

当然,在这两种操作中,总是存在程序流因某些情况而无限期停止的风险——比如计时器的使用——但我想知道关于这一问题的一些更权威的意见


我必须承认,这个问题的定义相当模糊,但我想听听关于何时使用其中一个的一些建议。在使用MS Visual Studio调试我正在使用的POP3客户端中的异步SSL操作时,我遇到了一些问题,有时我认为在这种情况下使用异步可能不是一个好主意。

我认为选择同步/异步是非常特定于应用程序的。我同意异步范例可以使代码和调试更加复杂,但它确实有它的好处

举例来说,我们从同步IO切换到使用异步IO的boost asio的主要原因是,在我们的应用程序中,阻止IO不是一个选项,我们有一个多媒体流服务器,在其中,我将编码后的流媒体数据包传输到多个客户端。问题是网络问题导致整个捕获编码传递管道被有效地暂停(例如,如果与单个客户端的连接失败)

总而言之,在我(有限公司)使用异步IO的经验中,在等待IO完成的同时还有其他工作需要完成的情况下(例如为其他客户机服务等),异步IO非常有用。在需要等待IO结果继续的系统或场景中,只使用同步IO要简单得多

它在双工通信系统中也有意义(例如,更复杂的协议,如SIP、RTSP,其中客户端和服务器都可以发送请求)。我已经有一段时间没有讨论POP了,但是对于您示例中的简单交换,异步IO可能被认为是过火了。只有当我确信同步IO不足以满足我的需求时,我才会切换到异步IO

通过阅读boost asio文档,我发现掌握窍门的最佳方法是通过示例进行操作。另外,你可能想查看的一个链接是,它有一个关于boost asio的非常好的章节。还有一些非常优秀的文章值得一看。

在解释这两个概念方面做得非常出色。Chris也有一个很好的博客描述异步概念。解释超时如何工作的示例特别有趣,示例也是如此

首先考虑同步连接操作:

这里的控制流相当简单,您的程序调用一些API(1)来连接套接字。API使用I/O服务(2)在操作系统(3)中执行操作。一旦此操作完成(4和5),控制将立即返回到程序(6),并带有一些成功或失败的指示

类似的异步操作具有完全不同的控制流:

在这里,应用程序使用相同的I/O服务(2)启动操作(1),但控制流是反向的。操作的完成会导致I/O服务通过完成处理程序通知您的程序。从步骤3到操作完成之间的时间完全包含在同步案例的连接操作中

您可以看到,对于大多数程序员来说,同步情况自然更容易掌握,因为它代表了传统的控制流范例。异步操作使用的反向控制流很难理解,它通常会迫使程序将操作拆分为
start
handle
方法,在这些方法中逻辑会发生变化。然而,一旦你对这个控制流有了基本的了解,你就会意识到这个概念是多么强大。异步编程的一些优点是:

  • 将线程与并发性分离。以长时间运行的操作为例,对于同步情况,您通常会创建一个单独的线程来处理该操作,以防止应用程序的GUI变得无响应。这个概念在小范围内运行良好,但在一些线程中很快就会崩溃

  • 提高性能。每个连接的线程设计根本无法扩展。看

  • 组合(或链接)。高级操作可以由多个完成处理程序组成。考虑转移JPEG图像,协议可能规定前40个字节包括描述图像大小、形状、或一些其他信息的头部。发送此标头的第一个完成处理程序可以启动发送图像数据的第二个操作。更高级别的操作
    sendImage()
    不需要知道或关心用于实现数据传输的方法链接

  • 超时和取消功能。有一些特定于平台的方法来超时长时间运行的操作(例如:
    SO\u RCVTIMEO
    SO\u SNDTIMEO)<
    
    //<1> Resolve the host
    async_resolve(session->resolver_, host, port)
    
    .then([=](tcp::resolver::results_type &results) {
        //<2> Connect to the host
        return async_connect(session->socket_, results);
    
    }).then([=]() {
        //<3> Write the request
        return async_write(session->socket_, session->req_);
    
    }).then([=](std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);
        //<4> Read the response
        return async_read(session->socket_, session->buffer_, session->res_);
    
    }).then([=](std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);
        //<5> Write the message to standard out
        std::cout << session->res_ << std::endl;
    
    }).then([]() {
        //<6> success, return default error_code
        return boost::system::error_code();
    }, [](const boost::system::error_code err) {
        //<6> failed, return the error_code
        return err;
    
    }).then([=](boost::system::error_code &err) {
        //<7> Gracefully close the socket
        std::cout << "shutdown..." << std::endl;
        session->socket_.shutdown(tcp::socket::shutdown_both, err);
    });
    
    asio::io_context io_context;
    asio::ip::tcp::acceptor acceptor;
    ...
    asio::ip::tcp::socket sock(io_context);
    acceptor.accept(sock);