2014-04-15 43 views
0

簡單實現異步多線程TCP服務器。 以滿足的方式工作,但內存使用量不斷增加。 調查後我發現那個內存都是沒有被清理的線程對象。 但是,線程確實將「主機斷開連接」寫入日誌文件,因爲這是該線程要執行的最後一行代碼,所以我希望它自行清理。但是這似乎並沒有發生。對於所做的每一個連接,都會創建一個線程並停止運行,但它永遠不會完全清理。線程在完成執行後未被清除

這是怎麼回事?

沒有例外正在要麼產生。

private void AcceptNextClient() 
    { 
     if (acceptConnections) serverSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), serverSocket); 
    } 


    private void AcceptTcpClientCallback(IAsyncResult ar) 
    { 
     try 
     { 
      TcpListener serverSocket = (TcpListener)ar.AsyncState; 
      TcpClient clientConnection = serverSocket.EndAcceptTcpClient(ar); 
      new Thread(unused => HandleClientCommunication(clientConnection)).Start(); 
     } 
     catch (Exception ex) { Disk.AppendLog(ex.ToString()); } 
     AcceptNextClient(); 
    } 

    private void HandleClientCommunication(TcpClient tcpClient) 
    { 
     string hostName = ""; 
     try 
     { 
      using (StreamWriter sw = new StreamWriter(tcpClient.GetStream())) 
      using (StreamReader sr = new StreamReader(tcpClient.GetStream())) 
      { 
       hostName = Dns.GetHostEntry(((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address).HostName; 
       bool read = true; 
       while (read) 
       { 
        string buffer = sr.ReadLine(); 
        if (buffer == null) read = false; 
        else 
        { 
         if (buffer.ToUpper().Equals("CLOSE_CONNECTION")) read = false; 
         else 
         { 
          sw.WriteLine(buffer); 
          sw.Flush(); 
         } 
        } 
       } 
       tcpClient.Close(); 
      } 
     } 
     catch (Exception ex) 
     { 
      Disk.AppendLog(hostName + " " + ex.ToString()); 
     } 
     Disk.AppendLog("host disconnected"); 
    } 

    public static void AppendLog(string msg) 
    { 
     File.AppendAllText(exePath + "errors.log", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + " " + msg + Environment.NewLine); 
    } 
+1

「一個線程被創建並停止運行,但它永遠不會完全清理。」究竟是什麼讓你得出這樣的結論呢?並且說「因爲記憶力增加」。你有沒有試過內存分析器? –

+0

是的,我嘗試JetBrains .dotTrace,並且進程資源管理器有一個關於.NET性能的選項卡,.NET CLR LocksAndThreads,我可以清楚地看到客戶端連接時線程對象的數量。當他們斷開連接時永遠不會退縮。在第一次啓動該程序時,大約是7MB,但幾天後它超過了100MB,並且仍在增長。做一個GC.Collect()也不會清理它們。 –

+0

沒有任何顯示如果「主機已斷開連接」已被寫入,它將保持線程。我的懷疑是,沒有任何內存壓力讓它值得做一個完整的收集。你嘗試強制GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced); GC.WaitForPendingFinalizers();'?注意:你不應該把它留在生產代碼中 - 它只是幫助確定這是否是原因。 –

回答

0

所以我發現答案後,注意到垃圾收集器不能運行時,程序作爲服務運行,但運行時(並解決所有內存問題),當它作爲窗體運行時。

卸下固定的所有問題的[STAThread]在主(字符串[]參數)。代碼本身沒有任何問題。

http://support.microsoft.com/kb/828988

0

在這裏,我的猜測是,如果你使用線程這樣

new Thread(unused => HandleClientCommunication(clientConnection)).Start(); 
     } 

,如果你有讓我們假設100個呼叫每秒,你將結束與100 MB以上,至少在你的應用程序,因爲每個線程至少需要1MB才能記住垃圾收集器不能保證即使線程完成執行也總是收集內存。
我可以在這裏建議的是切換所有代碼以使用任務。
您的代碼將是這個樣子

Task.Factory.StartNew(unused => HandleClientCommunication(clientConnection)); 
     } 

這裏爲什麼你應該使用任務交給線程(當它的可能)

  • 創建和銷燬一個線程在時間
  • 方面 一個昂貴的操作
  • 具有大量線程的浪費存儲器資源並也傷害由於性能 具有調度和運行線程之間的上下文切換的操作系統
  • 任務安排在一個線程池中。具體的線程數取決於所使用的調度器。如果你的應用程序提出了很多線程池的請求,線程池將嘗試使用這一個線程服務所有的請求 。但是,如果您的應用程序正在排隊多個要求比線程池線程可以處理它們更快的請求,則會創建其他線程。
+0

試過了,現在線程對象的數量增加得不是很快,但是沒有更多的線程正在上升。 –

+0

這是正常的,因爲你有很多創建的線程,你不能控制,所以最好的辦法是嘗試限制連接數在某些時候使用Qeue 類爲例 –

+0

這是不正常的我很害怕,儘管所有當前連接都已關閉,但線程數量仍在不斷增加並保持較高水平。 –

相關問題