据我所知,同步和异步操作之间的主要区别。即write()
或read()
vs async_write()
和async_read()
是前者,直到操作完成后才返回-or error-,最后一个,立即返回。< / p>
由于异步操作由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>
同步/异步运算符之间的区别没有多大意义。
当然,在这两种操作中,总是存在程序流程在某些情况下无限期停止的风险 - 使用计时器 - 但我想知道更多这方面的授权意见。
我必须承认这个问题定义不明确,但我想听听一些关于何时使用其中一个的建议。在使用MS Visual Studio调试关于我正在使用的POP3客户端中的异步SSL操作时,我遇到了问题,有时候认为在这个中使用异步可能是个坏主意。
答案 0 :(得分:33)
Boost.Asio documentation解释这两个概念真的很棒。正如Ralf mentioned, Chris还有一个描述异步概念的博客。解释超时工作方式的parking meter示例特别有趣,bind illustrated示例也是如此。
首先,考虑同步连接操作:
这里的控制流程相当简单,你的程序会调用一些API(1)来连接套接字。 API使用I / O服务(2)在操作系统(3)中执行操作。一旦完成此操作(4和5),控制将立即返回到您的程序(6),并指示成功或失败。
类似的异步操作具有完全不同的控制流程:
此处,您的应用程序使用相同的I / O服务(2)启动操作(1),但控制流程已反转。完成操作会导致I / O服务通过完成处理程序通知您的程序。步骤3和操作完成之间的时间完全包含在同步情况的连接操作中。
您可以看到同步案例对于大多数程序员来说自然更容易掌握,因为它代表了传统的控制流程范例。异步操作使用的反向控制流很难理解,它常常会迫使程序将操作拆分为逻辑移位的start
和handle
方法。但是,一旦您对此控制流程有了基本的了解,您就会意识到这个概念实际上有多强大。异步编程的一些优点是:
将线程与并发分离。进行长时间运行操作,对于同步情况,您通常会创建一个单独的线程来处理操作,以防止应用程序的GUI无响应。这个概念在小范围内工作正常,但很快就会在少数线程中崩溃。
提高效果。每个连接的线程设计根本无法扩展。请参阅C10K problem。
作文(或链接)。更高级别的操作可以由多个完成处理程序组成。考虑传输JPEG图像,协议可能规定前40个字节包括描述图像大小,形状的标题,也许还有一些其他信息。发送此标头的第一个完成处理程序可以启动第二个操作以发送图像数据。更高级别的操作sendImage()
不需要知道或关心用于实现数据传输的方法链。
超时和取消能力。有特定于平台的方法可以超时长时间运行(例如:SO_RCVTIMEO
和SO_SNDTIMEO
)。使用异步操作可以在所有支持的平台上使用deadline_timer
取消长时间运行的操作。
当然,在这两个行动中都有 总之,程序流动的风险 一些人无限期地停下来 circunstance-使用 计时器 - 但我想知道一些 更多的授权意见 物质
我使用Asio的个人经验源于可扩展性方面。在处理有限的资源(如内存,线程,套接字等)时,编写supercomputers软件需要相当多的谨慎。使用每个连接线程约200万个同时操作是一种设计在到达时死亡
答案 1 :(得分:3)
我认为同步/异步的选择非常特定于应用程序。我同意异步范例可以使代码和调试更加复杂,但它确实有它的好处。
为了说明,我们使用异步IO从同步IO切换到boost asio的主要原因是在我们的应用程序中阻止IO不是一个选项,我们有一个多媒体流服务器,我在其中将媒体数据包流式传输到多个客户端在编码之后。问题是网络问题导致整个捕获编码传递管道被有效停止(例如,如果与单个客户端的连接失败)。
总而言之,在我的(异步IO)经验中,它可以在您等待IO完成时需要完成其他工作的情况下(例如服务其他客户端等)。在系统或方案中,您必须等待IO的结果继续,使用同步IO会更加简单。
在双工通信系统中也是有意义的(例如,SIP,RTSP等更复杂的协议,客户端和服务器都可以发送请求)。自从我处理POP以来已经有一段时间了,但是对于你的例子中的简单交换,异步IO可能被视为过度杀伤。只有在我确定同步IO不足以满足我的要求时,才会切换到异步IO。
写一下提升asio文档,我发现获得它的最好方法是完成这些示例。此外,您可能想要查看的链接是http://en.highscore.de/cpp/boost/index.html它有一个关于boost asio的非常好的章节。另外Chris Kohlhoff's (author of asio) blog还有一些非常好的文章值得一试。
答案 2 :(得分:2)
同步易于控制程序流。
异步具有更好的性能,因为它不需要保存/恢复光纤任务的寄存器。
异步使用回调,难以编程。我们可以尝试promise-cpp使异步流像 synchronous -
http客户端示例-
//<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);
});
答案 3 :(得分:0)
一个简单的答案是:( here引用,几乎没有修改)
在同步客户端中,所有内容都以串行方式发生。如果您发送命令,它将排在前一个命令之后,直到它们完成为止。通常是单线程应用程序。
在ASync中,所有事情都会同时发生。通常是同时承担多项任务的多线程应用程序。
答案 4 :(得分:0)
根据 Ralf 的帖子,我认为您需要使用 Asio 异步 API 的唯一情况是您不希望您的程序在套接字上阻塞
例如,此服务器将在 accept() 调用中阻止您的程序
asio::io_context io_context;
asio::ip::tcp::acceptor acceptor;
...
asio::ip::tcp::socket sock(io_context);
acceptor.accept(sock);
如果您的服务器程序需要做“其他事情”(例如 GUI),那么这显然是不可接受的。
另一方面,如果您的程序此时所做的只是等待传入的连接,那么我认为使用异步 API 是没有意义的(不需要它,不要使用它)
我很想听听关于我上面的“唯一需要它的情况”假设的任何评论,因为我设计了一个包含大约 5 个不同服务器/客户端程序的大规模架构,这些程序在相当程度上来回同步调用到目前为止,在测试中可以完美运行的大量消息数据流 (HTTP)。
所以,我想听听我为什么需要进行异步调用的争论(因为一位同事提议更改为异步调用)。
关于程序的多线程,优缺点一样吗?
参考资料