【问题标题】:Asynchronous TCP Communication in .NET.NET 中的异步 TCP 通信
【发布时间】:2010-10-08 23:01:40
【问题描述】:

这里的快速问题:与 NetworkStream 类(由 TcpClient 生成)使用异步通信有什么明显的好处,即 BeginRead/BeginWrite 方法,而不是运行一个单独的线程并对其使用同步操作,即读/写?我的印象是(这很可能是完全错误的)异步操作是非阻塞的,并且在操作系统级别(可能在 TCP 堆栈中?)执行,并带有一个用于回调的线程池项。我认为它肯定与在同步方法上调用 ThreadPool.QueueUserWorkItem 不同,否则提供它就没有什么意义了。现在,我相当有信心这至少是文件 I/O 发生的事情(操作系统级调用),但如果有人能澄清有关网络(TCP)通信的问题,那将是最有帮助的。基本上,我想知道这两种方法是否有任何特定的好处(除了能够将 BinaryReader/StreamReader 类与同步调用一起使用这一显而易见的好处)。

【问题讨论】:

    标签: .net networking sockets asynchronous tcp


    【解决方案1】:

    有一个区别,如果你使用一个 Worker 线程来调用同步版本,你会在阻塞调用中占用一个线程。

    Begin 方法不会占用线程,而是在适当的 I/O 信号上使用回调,然后回调将在池中的线​​程上运行。

    【讨论】:

    • 对,这正是我所怀疑的。因此,您会建议在可能的情况下在单独的线程上使用 Begin 方法而不是同步方法?这样做似乎更优雅,如果不是更有效的话。
    • .NET 框架实现低级(BeingXX/EndXX)模式一般使用IO Completion 端口。这是 Windows 下可用的最具可扩展性的 IO 方法。
    • @Noldorin:是的,我建议使用 Begin 方法,主要是因为这样代码更简单,很高兴在这种情况下,更简单的方法也更有效。
    • @Richard:很高兴得到一些确认,干杯。 @AnthonyWJones:非常感谢,答案是你的。
    【解决方案2】:

    我同意 AnthonyWJones 的观点,假设您的线程池有 10 个线程,但您有 100 个被动足够的客户端。使用异步调用,您可以为其中的每一个调用 BeginRead,当来自某个调用的数据准备好时,它将由池线程之一处理。但如果您尝试使用 QueueUserWorkItem,您将安排仅从 10 个客户端接收数据。如果他们在 1 小时内什么都不发送,其他 90 个客户端将永远没有机会获取数据。

    【讨论】:

    • 确实,我看到调用 Begin 方法显然不等同于现在的 QueueUserItmem...不过,重点是它与运行具有同步操作的单独线程相比如何。
    • 如果您明确使用池中的线程或自己创建多个线程,则没有太大区别 - 任何方式都限制了线程数量...使用异步操作,仅使用线程做真正的工作,然后释放。使用同步操作,线程可能会被空闲等待阻塞。
    【解决方案3】:

    我不太确定为什么 NetworkStream 甚至有 BeginRead/Write,因为这首先基本上违反了 NetworkStream 的目的。通过使用 Async 方法,您可以获得更快的响应、更大的可扩展性并减少资源消耗。

    如果您一次只能有一个连接,那么是否使用线程池线程并不重要,但如果您接受许多连接,那么您肯定要使用异步。

    【讨论】:

    • 我明白你的意思。这些异步方法可能更适合在 TcpClient 类中。似乎共识稍微倾向于使用异步方法来获得更好的性能(如果只是略微)。
    【解决方案4】:

    正如其他人所指出的,当您使用同步方法时,为您的进程分配另一个线程会影响您,因为您会增加同时连接的数量。

    但是,如果您知道您只会有少量连接,我会说它变成了清洗,您应该选择最适合您的应用程序的方法。

    在线程开销可以忽略不计的情况下,我希望这两种情况会像这样上演。

    异步:

    1. 您调用 BeginRead / BeginWrite
    2. “系统”(框架/操作系统)被告知您想要什么
    3. 读/写完成
    4. “系统”告诉线程池中的一个线程调用你的回调
    5. 你做任何你需要做的事情来完成操作

    在另一个线程中同步:

    1. 你从线程池中获取一个线程来处理 IO
    2. 你调用读/写
    3. “系统”(框架/操作系统)被告知您想要什么
    4. 读/写完成
    5. 你做任何你需要做的事情来完成操作

    这里唯一的区别是异步调用的第 4 步变成了另一个线程情况下的同步调用的第 1 步。

    【讨论】:

    • 是的,这基本上是我的另一个考虑因素。嗯,现在我有我开始时相同的两个相反的观点......
    • 一个线程,即使被阻塞,也是一个重要的资源(从进程的 VM 空间分配的 1MB 堆栈空间开始)。
    • 我会在一定程度上购买它,但由于我们正在讨论从这里的池中获取线程,我不知道这有多大关系。我倾向于为申请做任何最自然的事情,除非我能证明我有问题。过早的优化和所有...
    • 区别在于可扩展性。对于一个连接没有太大区别,对于 100 有。当您处理 10,000 个连接时,同步方法根本不起作用。而且操作系统有很多“特殊魔法”,这意味着异步版本非常高效。
    • 这将是一个可证明的问题。我想我们都同意:-)
    猜你喜欢
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2013-09-07
    • 2012-07-29
    • 1970-01-01
    • 2017-05-21
    • 1970-01-01
    • 2010-09-19
    相关资源
    最近更新 更多