C# 完成执行后未清理的线程

C# 完成执行后未清理的线程,c#,multithreading,asynchronous,tcplistener,C#,Multithreading,Asynchronous,Tcplistener,异步多线程TCP服务器的简单实现。 按预期工作,但内存使用率不断上升。 经过调查,我发现内存都是未清理的线程对象。 但是,线程确实会将“主机断开连接”写入日志文件,因为这是该线程要执行的最后一行代码,所以我希望它会自行清理。但这似乎没有发生。对于建立的每个连接,都会创建一个线程并停止运行,但它不会完全清理 发生了什么事 也没有生成任何异常 private void AcceptNextClient() { if (acceptConnections) server

异步多线程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);
    }

我猜如果你用这样的线

new Thread(unused => HandleClientCommunication(clientConnection)).Start(); 
        }
Task.Factory.StartNew(unused => HandleClientCommunication(clientConnection)); 
        }
如果你每秒有100次呼叫,那么你的应用程序中至少会多出100 MB,因为每个线程至少需要1MB。请记住,垃圾收集器不能保证即使线程已完成执行,也会始终收集内存。
我建议您将所有代码切换为使用任务。
您的代码将如下所示

new Thread(unused => HandleClientCommunication(clientConnection)).Start(); 
        }
Task.Factory.StartNew(unused => HandleClientCommunication(clientConnection)); 
        }
这里是为什么您应该使用线程上的任务(如果可能的话)

  • 创建和销毁线程是一项代价高昂的操作 时间
  • 拥有大量的线程会浪费内存资源,而且还会由于内存不足而影响性能 操作系统必须在可运行线程之间进行调度和上下文切换
  • 任务在线程池中调度。具体的线程数取决于使用的调度程序。如果应用程序对线程池发出许多请求,线程池将尝试为所有线程提供服务 请求只使用这一个线程。但是,如果应用程序正在排队等待多个请求 比线程池线程处理它们的速度更快,将创建更多线程

因此,在注意到垃圾收集器在程序作为服务运行时无法运行,但在作为窗体运行时确实运行了(并解决了所有内存问题)之后,我找到了答案

删除Main(string[]args)上的[STAThread]修复了所有问题。代码本身没有任何问题


“一个线程被创建并停止运行,但它从未完全清理。”到底是什么让你得出这样的结论?并且一定要说“因为记忆增加了”。你试过内存分析器吗?是的,我试过JetBrains.dotTrace,process explorer有一个关于.NET性能、.NET CLR锁和线程的选项卡,在那里我可以清楚地看到在客户端连接时线程对象数量的增加。当他们断开连接时,再也不会返回。第一次启动该程序时,它的容量约为7MB,但几天后它就超过了100MB,并且还在增长。执行GC.Collect()也不会清理这些线程。如果写入了“主机断开连接”,则没有显示任何可以保持线程正常运行的内容。我的怀疑是,根本没有任何记忆压力使它值得做一次完整的收集。您是否尝试强制执行
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);GC.WaitForPendingFinalizers()?注意:您不应该将其保留在生产代码中-这只是为了帮助确定这是否是原因。要检查的另一件事:线程是否从
磁盘返回。AppendLog
?当我将其作为服务运行时,两个GC方法都不会清理线程。我正在GC之前和之后记录一条消息。如果我只执行GC.Collect(),则在登录之前和之后都执行。如果我和waitforfinalizers一起做另一个,第二个永远不会被记录。但是,如果我不将其作为服务运行,那么两个GC-collect方法在清理线程对象的同时也能完美地工作。这到底是怎么回事@Marc Graveltried,现在线程对象的数量增长速度变慢了,但仍然在增长。这是正常的,因为创建了很多线程,无法控制,所以最好的方法是尝试在某个时间使用QUE类限制连接的数量,例如,这恐怕不正常,尽管当前所有连接都已关闭,但线程数仍在不断增加并保持高位。有多少请求有多少线程尝试进行统计没有统计数据,我正在测试它。每一个请求都是我自己提出的。以前每个请求只有一个线程,现在有点模糊了。看起来只要我不断发出一致的请求,线程的数量就不会增加。但是,如果我等几秒钟再做一个新的。客户端将挂起1-2秒。然后在服务器上创建一个新的线程对象,然后客户端获得响应。如果我在所有请求之间留出大约4秒的时间,那么每次都会创建一个新线程。如果两个请求之间的时间更短,则不会生成另一个线程对象