【问题标题】:C#: Handling terminate signal in TCP handler thread?C#:在 TCP 处理程序线程中处理终止信号?
【发布时间】:2009-12-08 11:07:51
【问题描述】:

我在 C# 中使用 TcpClient 类。

每次有新的tcp连接请求,通常的做法是创建一个新线程来处理它。并且主线程应该可以随时终止这些处理程序线程。

我对每个处理程序线程的解决方案如下:

1 Check NetworkStream's DataAvailable method
    1.1 If new data available then read and process new data
    1.2 If end of stream then self terminate
2 Check for terminate signal from main thread
    2.1 If terminate signal activated then self terminate
3 Goto 1.

这种轮询方法的问题在于,所有这些处理程序线程都将占用大量处理器资源,尤其是在这些线程数量巨大的情况下。这使得它非常低效。

有更好的方法吗?

【问题讨论】:

  • 我不小心点击了那个选项,无法改回来!!!

标签: c# tcp polling tcpclient terminate


【解决方案1】:

请参阅Asynchronous Server Socket Example 了解如何以“.NET 方式”执行此操作,而无需为每个请求创建新线程。

【讨论】:

  • 但是,异步 I/O 方式并没有提供提供终止的方式。假设由于某种原因,用户想要终止应用程序,并且有一些异步调用尚未调用相应的回调函数,因为尚未发生传入数据,但必须立即终止应用程序,不得延迟。
  • 嗯,异步 IO 将在内部使用线程(池)而不是创建它们。因此,我认为,如果您有大量长期连接,最好为每个连接使用一个专用线程,而不是使用线程池,因为使用 ThreadPool 的其他应用程序可能会受到此影响。
  • @Lopper:终止的一种方法是不开始下一个操作。读完了吗?不要再做一次 BeginRead。我还怀疑关闭 TcpClient 实例会取消。
  • @A9S6:Async IO 只会在有事可做时使用线程池线程。否则,根本不使用线程。如果回调期间执行的处理时间过长,那将是不在 ThreadPool 线程上执行此操作的原因。请记住,异步 IO 永远不会阻塞任何线程 - 甚至不会阻塞 ThreadPool 线程。
【解决方案2】:

信不信由你,1000 滴答睡眠真的会让事情顺利进行。

private readonly Queue<Socket> sockets = new Queue<Socket>();
private readonly object locker = new object();
private readonly TimeSpan sleepTimeSpan = new TimeSpan(1000);
private volatile Boolean terminate;

private void HandleRequests() 
{
    Socket socket = null;

    while (!terminate)
    {
        lock (locker)
        {
            socket = null;
            if (sockets.Count > 0)
            {
                socket = sockets.Dequeue();
            }
        }

        if (socket != null)
        {
            // process
        }

        Thread.Sleep(sleepTimeSpan);
    }   
}

【讨论】:

  • 如果我错了,请纠正我,但上面的代码意味着只有一个线程将执行来自 tcp 连接的传入数据的读取/处理。如果处理 if(socket!=null) 块内的数据需要大量时间,那么我想在处理另一个连接之前会有很大的延迟。有没有更好的方法可以并行处理每个套接字但同时不使用轮询方法?
  • 不不,这是您在创建处理程序线程时传递给它们的方法。
【解决方案3】:

我记得我曾经做过类似的 Windows 服务。它是一个 NTRIP 服务器,可以接收大约 1000 个 TCP 连接并将数据路由到 NTRIP Caster。

如果您为此应用程序有一个专用服务器,那么除非您向每个线程(文件 IO、数据库等)添加更多代码,否则这将不是问题——尽管在我的情况下,我也有数据库处理来登录/注销每个连接)。

注意事项:

  1. 线程达到 600 左右时的带宽。当 TCP 缓冲区窗口由于某种原因被阻塞或可用带宽不足时,您将开始看到断开连接
  2. 您运行此应用程序的操作系统可能有一些限制,这可能会导致断开连接

以上内容可能不适用于您的情况,但我只是想把它放在这里,因为我在开发过程中遇到过。

【讨论】:

    【解决方案4】:

    您是对的,您不希望所有线程都“忙于等待”(即一遍又一遍地运行一个小循环)。您要么希望它们阻塞,要么希望使用异步 I/O。

    正如 John Saunders 所提到的,异步 I/O 是执行此操作的“正确方法”,因为它可以扩展到数百个连接。基本上,您调用 BeginRead() 并将其传递给回调函数。 BeginRead() 立即返回,当数据到达时,在线程池中的一个线程上调用回调函数。回调函数处理数据,再次调用 BeginRead(),然后返回,将线程释放回池中。

    但是,如果您一次只打开少数几个连接,则为每个连接创建一个线程是非常好的。不要在循环中检查 DataAvailable 属性,而是继续调用 Read()。线程将阻塞,不消耗 CPU,直到有数据可供读取。如果连接丢失,或者您从另一个线程将其关闭,则 Read() 调用将引发异常,您可以通过终止读取器线程来处理该异常。

    【讨论】:

    • 但是,异步 I/O 方式并没有提供提供终止的方式。假设由于某种原因,用户想要终止应用程序并且有一些异步调用尚未调用相应的回调函数,因为尚未发生传入数据。
    猜你喜欢
    • 1970-01-01
    • 2013-07-26
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多