为什么SocketAsyncEventArgs的Completed回调经常在新创建的线程中执行而不是使用有界线程池?

时间:2014-08-12 16:15:29

标签: c# multithreading threadpool winsock asyncsocket

我有一个简单的客户端应用程序,它以低吞吐量从网络接收字节缓冲区。这是代码:

private static readonly HashSet<int> _capturedThreadIds = new HashSet<int>();

private static void RunClient(Socket socket)
{
    var e = new SocketAsyncEventArgs();
    e.SetBuffer(new byte[10000], 0, 10000);
    e.Completed += SocketAsyncEventsArgsCompleted;

    Receive(socket, e);
}

private static void Receive(Socket socket, SocketAsyncEventArgs e)
{
    var isAsynchronous = socket.ReceiveAsync(e);
    if (!isAsynchronous)
        SocketAsyncEventsArgsCompleted(socket, e);
}

private static void SocketAsyncEventsArgsCompleted(object sender, SocketAsyncEventArgs e)
{
    if (e.LastOperation != SocketAsyncOperation.Receive || e.SocketError != SocketError.Success || e.BytesTransferred <= 0)
    {
        Console.WriteLine("Operation: {0}, Error: {1}, BytesTransferred: {2}", e.LastOperation, e.SocketError, e.BytesTransferred);
        return;
    }

    var thread = Thread.CurrentThread;
    if (_capturedThreadIds.Add(thread.ManagedThreadId))
        Console.WriteLine("New thread, ManagedId: " + thread.ManagedThreadId + ", NativeId: " + GetCurrentThreadId());

    //Console.WriteLine(e.BytesTransferred);

    Receive((Socket)sender, e);
}

应用程序的线程行为非常好奇:

  1. SocketAsyncEventsArgsCompleted方法经常在新线程中运行。我原本预计在一段时间后不会创建新的线程。我原本期望线程被重用,因为线程池(或IOCP线程池)以及吞吐量非常稳定。
  2. 线程数保持不变,但我可以在进程资源管理器中看到经常创建和销毁线程。同样,我也不会想到创建或销毁线程。
  3. 你能解释一下应用程序的行为吗?

    编辑:“低”吞吐量是每秒20条消息(大约200 KB / s)。如果我将吞吐量增加到每秒超过1000条消息(50 MB / s),则应用程序行为不会改变。

2 个答案:

答案 0 :(得分:6)

低应用程序吞吐量本身无法解释线程创建和销毁。套接字每秒接收20条消息,这足以使线程保持活动状态(等待线程在闲置10秒后被销毁)。

此问题与线程池线程注入有关,即线程创建和销毁策略。线程池线程定期注入并销毁,以便衡量新线程对线程池吞吐量的影响。

这称为线程探测。第9频道视频CLR 4 - Inside the Thread Pool清楚地解释了这一点(跳至26:30)。

似乎线程探测总是使用新创建的线程完成,而不是将线程移入和移出池。我认为对于大多数应用程序来说它的效果更好,因为它可以避免使未使用的线程保持活动状态。

答案 1 :(得分:2)

来自MSDN

  

从.NET Framework 4开始,线程池创建和   破坏工作线程以优化吞吐量,这是   定义为每单位时间完成的任务数。太少了   线程可能无法充分利用可用资源,同时也是如此   许多线程可能会增加资源争用。

     

注意

     

当需求低时,线程池线程的实际数量可以   低于最低值。

基本上,听起来你的低吞吐量会导致线程池破坏线程,因为它们不是必需的,只是占用了资源。我不担心。正如MS明确指出:

  

在大多数情况下,线程池的性能会更好   用于分配线程的算法。

如果您真的感到困扰,可以随时轮询ThreadPool.GetAvailableThreads()来观看池,并查看不同的网络吞吐量对它的影响。