【问题标题】:How do I shutdown a thread which contains a blocking call to NetworkStream.Read如何关闭包含对 NetworkStream.Read 的阻塞调用的线程
【发布时间】:2012-09-12 09:08:23
【问题描述】:

我正在查看一些不是我自己编写的代码,并试图了解导致应用程序崩溃的原因。

我认为这可能归结为它如何处理资源和管理线程。

主父线程创建一个新的应用层客户端(TCPMonitor),然后创建另一个处理 TCP 套接字通信的类(TCPListen)。此类产生一个新的线程循环,该循环创建一个新的 TCP 套接字客户端、该套接字上的读取网络流,然后调用同步阻塞 Read()。

但是,如果发生网络错误连接,则会在 TCPListen 线程循环中捕获异常,并将事件返回给所属类 TCPMonitor。 TCPMonitor 然后检查它是否拥有一个活动的 TCPListen 实例,如果是,它调用 Dispose() 并将该实例设置为 Null。

此时 TCPListen Read() 肯定仍处于阻塞调用中吗?如果是这种情况,如何确保从父线程调用 Dispose 将使子线程脱离阻塞调用并正确处理流和套接字?

【问题讨论】:

  • 我认为TCPListen 仍然会在Read 中被阻塞,如果有一个循环并且catch 块重新抛出异常但不退出循环。是这样吗?
  • 在读取完成后,套接字和流上的关闭方法周围有 try/catch 块,但它们不会被重新抛出。循环中的大多数代码也有更广泛的尝试/捕获,这也吞噬了错误。这会向父级引发一个事件,然后处理线程类。

标签: c# .net multithreading sockets


【解决方案1】:

关闭套接字以进行输入。这将解锁读取并使其获得 EOS 指示,无论 API 采用何种形式。就像您在 Windows 上一样,如果它继续发送,它也会导致另一端连接休息。 (此行为取决于平台。)

【讨论】:

    【解决方案2】:

    如何使用异步调用(例如BeginReceive)而不是阻塞调用,并让线程等待一个对象告诉它干净地终止?创建类似ManualResetEvent 的东西并让线程等待它。从主线程发出事件信号,当监听线程唤醒时,它可以关闭套接字。

    请注意,当使用数据调用回调委托时,如果线程需要接收更多数据,则需要再次调用BeginReceive。在此处查看BeginReceive 的描述:http://msdn.microsoft.com/en-us/library/dxkwh6zw

    【讨论】:

      【解决方案3】:

      您没有提到您的代码正在使用的实际 C# 网络 API 调用,但假设它是底层 Socket 对象上的众多“接收”方法之一,那么您是正确的,它将无限期地阻塞除非在 Socket 对象上设置了超时 (ReceiveTimeout),否则连接的套接字。

      一旦设置,如果在这段时间内没有收到数据,它将返回。然后,您可以将 Receieve 调用放在某个布尔值上的循环中,您可以在外部设置以触发线程停止。

      更新:

      您可以使用DataAvailable 在循环中查询 NetworkStream 以查看是否有要读取的内容,而不是阻塞同步读取调用上的线程?

      【讨论】:

      • 调用是一个简单的 NetworkStream.Read()。如果读取的超时时间是不确定的 (-1) 或设置为 5 分钟 (300000),那么线程将在读取时挂起。但是如果在线程类本身上调用了 dispose,那么实际的线程循环会发生什么?
      • 它将继续阻塞,如果您在 VS 中运行调试版本,我希望看到它继续显示为“正在运行”,即使程序似乎已经停止。我猜发布版本会不会特别优雅地拆除线程和套接字连接。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 2019-09-02
      • 2021-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多