2014-04-15 17 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

这是不正常的我很害怕,尽管所有当前连接都已关闭,但线程数量仍在不断增加并保持较高水平。 –

相关问题