Question

Simple implementation of an asynchronous multithreaded TCP server. Works as intented, except, memory usage keeps going up. After investigating I found that that memory were all thread objects not being cleaned up. However, the threads do write "host disconnected" to the log file, and since that's the last line of code for that thread to execute I expect it to clean up itself. But that doesn't seem to be happening. For each and every connection that is made, a thread is created and stops running but it but never completely cleans up.

What is going on?

No exceptions are being generated either.

    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);
    }
Était-ce utile?

La solution

So I found the answer, after noticing the Garbage Collector wasn't ever able to run while the program was being run as a service but did run (and solve all memory issues) when it was running as a Form.

Removing the [STAThread] at Main(string[] args) fixed all the issues. There wasn't anything wrong with the code itself.

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

Autres conseils

My guess here is if you use Thread like this

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

and if you have let's suppose 100 calls per second you will end up with 100 MB more at least in your App as each thread will require 1MB at least remember that garbage collector can not guarantee that memory will always be collected even if thread has finish the execution.
what I can suggest here is to switch all your code to use Task.
your code will look something like this

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

Here why you should use task over Thread (when it's possible)

  • Creating and destroying a thread is an expensive operation in terms of time
  • Having lots of threads wastes memory resources and also hurts performance due to the operating system having to schedule and context switch between the runnable threads
  • Tasks are scheduled on a pool of threads. The specific number of threads depends on the scheduler used.If your application makes many requests of the thread pool, the thread pool will try to service all of the requests using just this one thread. However, if your application is queuing up several requests faster than the thread pool thread can handle them, additional threads will be created.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top