我有一个简单的客户端应用程序,它以低吞吐量从网络接收字节缓冲区。这是代码:
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);
}
应用程序的线程行为非常好奇:
SocketAsyncEventsArgsCompleted
方法经常在新线程中运行。我原本预计在一段时间后不会创建新的线程。我原本期望线程被重用,因为线程池(或IOCP线程池)以及吞吐量非常稳定。你能解释一下应用程序的行为吗?
编辑:“低”吞吐量是每秒20条消息(大约200 KB / s)。如果我将吞吐量增加到每秒超过1000条消息(50 MB / s),则应用程序行为不会改变。
答案 0 :(得分:6)
低应用程序吞吐量本身无法解释线程创建和销毁。套接字每秒接收20条消息,这足以使线程保持活动状态(等待线程在闲置10秒后被销毁)。
此问题与线程池线程注入有关,即线程创建和销毁策略。线程池线程定期注入并销毁,以便衡量新线程对线程池吞吐量的影响。
这称为线程探测。第9频道视频CLR 4 - Inside the Thread Pool清楚地解释了这一点(跳至26:30)。
似乎线程探测总是使用新创建的线程完成,而不是将线程移入和移出池。我认为对于大多数应用程序来说它的效果更好,因为它可以避免使未使用的线程保持活动状态。
答案 1 :(得分:2)
来自MSDN
从.NET Framework 4开始,线程池创建和 破坏工作线程以优化吞吐量,这是 定义为每单位时间完成的任务数。太少了 线程可能无法充分利用可用资源,同时也是如此 许多线程可能会增加资源争用。
注意
当需求低时,线程池线程的实际数量可以 低于最低值。
基本上,听起来你的低吞吐量会导致线程池破坏线程,因为它们不是必需的,只是占用了资源。我不担心。正如MS明确指出:
在大多数情况下,线程池的性能会更好 用于分配线程的算法。
如果您真的感到困扰,可以随时轮询ThreadPool.GetAvailableThreads()
来观看池,并查看不同的网络吞吐量对它的影响。