TCP套接字连接中同步与异步的优势

7
我目前正在从Java背景学习C#。为了入门,我决定制作一个简单的SMTP邮件应用程序。我很快学会了C#支持同步和异步套接字。
就我所知,使用同步套接字与使用异步套接字并没有实质性的优劣之分,因为后者不会阻塞,因此不需要每次创建新线程。使用其中任何一种也不会带来明显的开销。
所以我的问题是:在大多数情况下,使用同步套接字是否有优势,还是最好坚持使用异步套接字?

1
这完全取决于情况,我会说在99%的情况下你会使用异步,在极少数的1%中,你想要确保电子邮件一定被发送并且该代码块已经完成后才继续程序。此外,如果你为此目的创建自己的线程,你也会使用同步。 - Vajura
我认为那是情理之中的事。但我也想到你可能也可以用异步来做同样的事情。或者我错了吗? - Mo H.
2
@MuhammadHijazi 你是对的,你可以这样做。从功能上讲,使用异步或同步套接字没有任何区别。 - dcastro
好的,完美!谢谢! - Mo H.
3个回答

10

异步IO可以节省线程。一个线程通常消耗1MB的堆栈内存,这是使用异步IO的主要原因,当并发的未完成IO操作数量变多时。根据我的测量结果,在线程数增加到数千个之前,操作系统的可伸缩性不会成为问题。

主要缺点是需要更多的开发工作才能使同一应用程序在相同可靠性水平下运行。

我已经详细论述了这种权衡。 另外:我们应该默认切换到使用异步IO吗?

建议总是使用异步IO是客观上错误的建议。


9
任何一种机制都可以工作。主要区别在于同步意味着阻塞一个本来可以做其他有用事情的线程,或者为每个连接专门分配一个线程。无论哪种方式,都不能很好地扩展。对于只有少量或仅有一个活动连接的简单应用程序,这可能还可以接受。
但是,对于需要处理大量并发连接的任何场景,异步API是提供足够性能的唯一选择。此外,在任何交互式场景(即需要处理用户输入和输出的场景)中,异步方法更容易与用户界面集成。特别是现在在C#中有了async和await。

请注意,为了正确实现同步API,意味着侦听套接字需要一个线程,每个客户端连接需要两个线程。 - Stephen Cleary
这取决于应用程序协议。简单的请求/响应协议不需要同时接收和发送,可以在单个线程中处理。 - Peter Duniho
只有当一切都完美无缺时,“简单”的请求/响应协议才能检测到半开放状态,至少需要一个计时器来恢复。 - Stephen Cleary
抱歉...我不知道你的意思。如何通过为连接添加第二个线程来解决半开放问题?除非尝试发送,否则仍然无法检测到该条件,如果协议要求本地端在接收数据之前不发送任何内容,则本地端没有理由尝试。也许您可以提供一个更详细的讨论链接,以阐述您所说的内容。 - Peter Duniho
抱歉,我指的是协议本身无法检测到半开连接,无论是同步还是异步。如果是同步的话,那么你需要一个定时器来关闭套接字(来自另一个线程),因此无论协议多么简单,严格单线程的方法都不可能实现。 - Stephen Cleary
是的,这是真的。有一些愚蠢的协议无法检测到半开连接。不过,这些协议可以使用每个连接只需一个线程的方式来实现。:) 至于使用计时器,单个计时器可以管理多个连接,因此仍然只需要一个专用于每个连接的线程。 - Peter Duniho

1
无论使用哪种编程语言,仅建议使用异步套接字是一个糟糕的建议。虽然所有问题都可以使用异步解决,但并非所有问题(例如100,000个连接)都可以使用同步解决。但大多数情况下,问题趋向于更简单(<100个连接)。
根据我的经验,(平庸的)程序员往往会在使用异步套接字时迷失在混乱中,而在单独的线程中处理同步套接字则更加简单、易懂和易于维护。在Windows下创建线程是昂贵的,在假定有一个适当的操作系统的情况下,它的开销要少得多(在x86 / Linux上为5微秒)。此外,它不需要1 MB的RAM来运行线程(至少对于本地程序来说),而只需要几kb的堆栈和状态(寄存器)。
此外,许多人认为同步套接字编程更慢,但这并不总是正确的。上下文切换(到其他线程)是有成本的,但是异步套接字界面对于操作系统内核也不便宜。
像往常一样:选择最适合情况的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接