线程停止执行其工作

Rox*_*Rox 3 c# multithreading backgroundworker

我们有一个 C# 应用程序,它连接到 FTP 服务器、下载一些文件、断开连接,并在一段时间后(由用户通过 UI 选择)重新连接并重复该过程。我们使用BackgroundWorker 实现了这一点,但我们注意到,运行较长时间后,程序停止在UI 和日志文件中记录其操作。那时,没有文件可供它下载,因此我们上传了一些文件,它恢复了活动,就好像什么也没发生一样。

问题是普通用户无法知道程序仍在运行,因此我们决定使用我们自己的线程来实现它。我们做了一个更简单的程序,以排除任何其他问题,这个程序只连接到 FTP 并断开连接。它像BackgroundWorker 一样停止显示消息(2 小时后一次,22 小时后一次,没有我们能找到的任何模式,并且在一台没有执行任何其他操作的计算机上)。

DoFTPWork += new DoFTPWorkDelegate(WriteFTPMessage);

FTPWorkThread = new Thread(new ParameterizedThreadStart(Process));

//seData is the FTP login info
FTPWorkThread.Start(seData);
Run Code Online (Sandbox Code Playgroud)

FTP方法是:

private void Process(object seData1)
{
    seData = (SEData)seData1;
    while (!stopped)
    {
        try
        {
            ftp = null;
            ftp = new FTP_Client();

            if (ftp.IsConnected)
            {
                logMessages += DateTime.Now + "\t" + "info" + "\t" + "Ftp disconnected from " + seData.host + "\r\n";
                ftp.Disconnect();
            }

            ftp.Connect(seData.host, 21);
            ftp.Authenticate(seData.userName, seData.password);
            logMessages += DateTime.Now + "\t" + "info" + "\t" + "Ftp connected to " + seData.host + "\r\n";

            error = false;
            logMessages += DateTime.Now + "\t" + "info" + "\t" + "Trying to reconnect in 5 seconds\r\n";
            System.Threading.Thread.Sleep(5000);
            SlaveEventArgs ev = new SlaveEventArgs();
            ev.Message = logMessages;
            txtLog.Invoke(DoFTPWork, ev);
            System.Threading.Thread.Sleep(200);
            logMessages = "";
        }

        catch (Exception ex)
        {
            logMessages = "";
            if (ftp.IsConnected)
            {
                ftp.Disconnect();
            }
            ftp.Dispose();
            logMessages += DateTime.Now + "\t" + "ERR" + "\t" + ex.Message + "\r\n";

            logMessages += DateTime.Now + "\t" + "info" + "\t" + "Trying to reconnect in 5 seconds\r\n";
            SlaveEventArgs ev = new SlaveEventArgs();
            ev.Message = logMessages;
            txtLog.Invoke(DoFTPWork, ev);
            System.Threading.Thread.Sleep(5 * 1000);
            error = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

WriteFTPMessage 在文本框中显示消息,并在写入 .txt 文件的原始程序中显示消息。

Spe*_*ort 5

如果我理解正确的话,这个while(!stopped)循环是运行几个小时的循环?如果是这种情况,您在哪里终止 ftp 连接(如果有的话)?在您发布的代码中关闭它的唯一一次是抛出异常,否则您只需取消引用该对象并创建一个新的对象,这是一个非常严重的资源泄漏,如果不是导致问题的话,至少会导致问题。

而且 ftp 似乎是全局可访问的。您是否使用不同的线程在任何地方访问它?对象线程安全吗?

编辑:

我在这里看到的最大问题是设计。并不是说我想攻击你或其他什么东西,而是你混合了各种各样的行动。线程、日志记录和 ftp 访问代码都在同一个函数中。

我的建议是重组你的程序。创建一个与以下类似的方法:

// Called by thread
void MyThreadOperation()
{
   while(!stopped)
   {
      // This is poor design in terms of performance.
      // Consider using a ResetEvent instead.
      Thread.Sleep(5000);

      try
      {
         doFTPDownload();
      }
      catch(Exception ex)
      {
         logMessage(ex.ToString());
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

doFTPDownload()应该是独立的。FTP 对象应在调用时在函数内创建并打开,并在完成之前将其关闭。logMessage()同样的概念也应该适用。我还建议使用数据库而不是文件来存储日志消息,以便锁定问题不会使问题变得复杂。

我知道这不是一个答案,因为您可能仍然会遇到问题,因为我无法确定可能的原因是什么。不过,我相信通过一些设计重组,您将能够更好地找到问题的根源。