前幾篇介紹的伺服器端監聽與客戶端連接,皆採「每連接一執行緒」模式:new Thread(...).Start()。頻繁建立/銷毀執行緒帶來顯著開銷。.NET 提供 ThreadPool 解決此問題,本文示範如何套用至 Socket 通訊。
Start()ThreadPool.QueueUserWorkItem(new WaitCallback(CallbackMethod));
QueueUserWorkItem:靜態方法,將工作項目排入佇列WaitCallback:委派簽名 void Method(object state),必須接受一個 object 參數clientThread = new Thread(new ThreadStart(ReceiveData));
clientThread.Start();
ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveData));
只需一行,且 ReceiveData 簽名需調整為 void ReceiveData(object state)。
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class TcpServer
{
private TcpListener _listener;
private bool _running;
public void Start(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
_running = true;
// 監聽迴圈也可用執行緒池
ThreadPool.QueueUserWorkItem(ListenLoop);
}
private void ListenLoop(object state)
{
while (_running)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
// 每個客戶端連接派遣到執行緒池處理
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
catch (SocketException)
{
// 監聽停止等
}
}
}
private void HandleClient(object state)
{
TcpClient client = (TcpClient)state;
NetworkStream stream = client.GetStream();
try
{
// 收發邏輯...
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
// 處理資料...
// 回應
byte[] response = System.Text.Encoding.ASCII.GetBytes("OK");
stream.Write(response, 0, response.Length);
}
finally
{
client.Close();
}
}
public void Stop()
{
_running = false;
_listener.Stop();
}
}
| 指標 | 手動執行緒 | 執行緒池 |
|---|---|---|
| 建立開銷 | 高 (核心物件配置、堆疊空間) | 低 (重用既有執行緒) |
| 記憶體壓力 | 每執行緒 1MB 預設堆疊 | 固定池大小 |
| 延遲 | 需建立後啟動 | 排入即執行 |
| 資源控制 | 難以限制最大並發 | ThreadPool.SetMaxThreads 可控制 |
Task.Run 或自建執行緒async/await + SocketAsyncEventArgs 或 NetworkStream.ReadAsync,比執行緒池更具擴展性六篇筆記涵蓋:
原始碼下載:本文配套原始碼