【问题标题】:Memory Leak with TCPClient in separate threadTCPClient 在单独线程中的内存泄漏
【发布时间】:2023-03-21 17:37:01
【问题描述】:

我正在尝试使用以下函数从网络流中读取一些字符串数据:

static TcpClient client;
static NetworkStream nwStream;

private void conn_start_Click(object sender, EventArgs e)
{
   //Click on conn_start button starts all the connections

   client = new TcpClient(tcp_ip.Text, Convert.ToInt32(tcp_port.Text));
   nwStream = client.GetStream();
   readBuff="";
   Timer.Start();
}

string readBuff;
private void readFromConnection()
{
  string x1 = "";
  byte[] bytesToRead = new byte[client.ReceiveBufferSize];
  int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
  x1 = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
  //
  //some more code to format the x1 string      
  //
  readBuff = x1;

  bytesToRead = null;
  GC.Collect();
}

现在 readFromConnection() 函数每秒从 Timer Tick 事件调用,代码如下:

private void Timer_Tick(object sender, EventArgs e)
{
   Thread rT1 = new Thread(readFromConnection);
   rT1.Start();
   //app crashes after 40-45 min, out of memory exception.
}

这会导致一些内存泄漏。运行 40-45 分钟后,应用程序因 OutOfMemory 异常而崩溃。

我的问题是是否有适当的方法来处理新线程,因为它只会存活 1 秒?我该如何克服这个问题?

我必须在新线程中运行此函数,因为在与 UI 相同的线程上时,它往往会冻结它。即使是很小的鼠标移动也需要几秒钟才能得到处理。

同样的观点,如果Tick事件在同一个线程中调用函数,则不存在内存泄漏的问题。

private void Timer_Tick(object sender, EventArgs e)
{
   readFromConnection();
   //No memory leak here.
} 

【问题讨论】:

  • 我有几个问题要问你,你为什么要手动拨打GC.Collect()?为什么要在Timer Tick Event 中创建线程?
  • 我添加了 GC 来检查它是否解决了任何内存泄漏问题。最初它不存在。对于新线程,这样做是为了防止主 UI 冻结。如果这样做是不好的方法,请告诉我?
  • 你不应该手动调用GC.Collect(),垃圾收集是为你管理的。你可以通过将你的逻辑包装在一个无限的while循环中来摆脱定时器,并在每次迭代之间添加一个睡眠指令,这样你就只剩下一个线程了。
  • 我会尽快添加答案

标签: c# multithreading memory-leaks tcpclient


【解决方案1】:

不需要Timer,您可以将Receive 逻辑放在一个无限while 循环中,并且可以在每次迭代之间为线程添加休眠指令。您不应该使用ReceiveBufferSize 从套接字流中读取数据。 ReceiveBufferSize 与对方发送了多少字节或我可以读取多少字节不一样。您可以使用Available 代替,即使当我从网络读取大文件时我也不信任此属性。可以使用client.Client.Receive方法,这个方法会阻塞调用线程。

private void readFromConnection()
{
    while (true)
    {
        if(client.Connected && client.Available > 0)
        {
            string x1 = string.Empty;
            byte[] bytesToRead = new byte[client.Available];
            int bytesRead = client.Client.Receive(bytesToRead);
            x1 = System.Text.Encoding.Default.GetString(bytesToRead);
        }
        Thread.Sleep(500);
    }
}

最后,关于GC看看this question

【讨论】:

  • 感谢改进建议。他们都工作得很好!
  • @chiro3110 好吧,很高兴能帮到你!
  • @Mahmoud 非常感谢。它解决了我一周的挣扎。
【解决方案2】:

只需使用Flush 内存流来防止内存泄漏。

private void readFromConnection()
{
  string x1 = "";
  byte[] bytesToRead = new byte[client.ReceiveBufferSize];
  int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
  x1 = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
  //
  //some more code to format the x1 string      
  //
  readBuff = x1;

  bytesToRead = null;
  nwStream.Flush(); // Add this line
  GC.Collect();
}

另外,使用Timer 和触发readFromConnection 是不好的方法。简单地说,使用while 语句来读取流。

【讨论】:

  • Flush 没有对内存泄漏做出任何改变,但改变方法的建议有所帮助。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-29
  • 2013-12-18
  • 1970-01-01
  • 1970-01-01
  • 2017-02-04
  • 2016-12-09
  • 2020-08-08
相关资源
最近更新 更多