我有一个简单的客户端应用程序,它以低吞吐量从网络接收字节缓冲区。这是代码:
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),应用程序行为不会改变。
最佳答案
应用吞吐量低本身并不能解释线程的创建和销毁。套接字每秒接收 20 条消息,这足以让一个线程保持事件状态(等待线程在空闲 10 秒后被销毁)。
此问题与线程池线程注入(inject)有关,即线程的创建和销毁策略。定期注入(inject)线程池线程和销毁,以衡量新线程对线程池吞吐量的影响。
这称为线程探测。第 9 channel 的视频中对此有清楚的解释 CLR 4 - Inside the Thread Pool (跳转到 26:30)。
线程探测似乎总是用新创建的线程完成,而不是将线程移入和移出池。我想对于大多数应用程序来说,它可以更好地工作,因为它避免了使未使用的线程保持事件状态。
关于c# - 为什么 SocketAsyncEventArgs 的 Completed 回调经常在新创建的线程中执行,而不是使用有界线程池?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25269235/