2013-05-16 42 views
0

我在其他问题中提到过,我正在使用进度条等工作ffmpeg解析器。C#有时会在运行线程时冻结表单

以下问题不是100%可重现。当我用我的程序转换视频文件时,大部分时间都可以。它可以在较小的文件(大约100MB)上完美工作,但在较大的文件(大约1GB)上,有时候gui会冻结,但ffmpeg正在运行(调试器仍然从ffmpeg获取输出并显示它,因此表单线程不冻结,只有控制台的GUI)

我也意识到,ffmpeg所做的工作不会造成任何可能导致死机的错误。

如果我在调试环境外运行它,表格也会冻结。

解析在额外的线程上处理ffmpeg异步的输出。

我认为这个线程冻结了,并且主程序在ffmpeg完成后会挂起,因为解析器得到一个command == null以及主程序将被传递完成转换。我实际上不能说这个,因为atm我不能重现冻结,而且最后一次忘记提及调试器是否输出"======== #"

你可以找到源头(在调试文件夹中的ffmpeg)here作为ConvertApp.zip

进程之间的谈话:

// message directors for threading 
    private BlockingCollection<string> commandsForParser = new BlockingCollection<string>(); 
    private BlockingCollection<string> commandsForMain = new BlockingCollection<string>(); 

    // sending commands to threads 
    public void SendCommmandParser(string command) 
    { 
     commandsForParser.Add(command); 
     //Debug.WriteLine("P: " + command); 
    } 
    public void SendCommmandMain(string command) 
    { 
     commandsForMain.Add(command); 
     //Debug.WriteLine("M: " + command); 
    } 

呼吁解析:

private void showParsedConsole() 
    { 
     ConsoleOutput dlg = new ConsoleOutput(); 
     dlg.Show(); 

     //... 

     while (true) 
     { 
      System.Windows.Forms.Application.DoEvents(); 

      string command = commandsForParser.Take(); 

      // if null, ffmpeg finished 
      if (command == null || command == "..break..") 
        { 
         SendCommmandMain("..break.."); 
         dlg.Close(); 
         break; 
        } 

      if (dlg.toClose) 
      { 
       SendCommmandMain("..cancel.."); 
       dlg.Close(); 
       break; 
      } 
      else if (command != null) 
      { 
       //... actualizing form (output, progress things) 
      } 
      else 
       dlg.addMessage("\n"); 
     } 
    } 

出发转换:

public string RunExternalExe(string info, string filename, string arguments = null) 
    { 
     // parse console in new thread 
     var thread = new Thread(showParsedConsole); 
     thread.Start(); 

     //... 

     #region init process to run 
      var process = new Process(); 

      process.StartInfo.FileName = filename; 
      if (!string.IsNullOrEmpty(arguments)) 
      { 
       process.StartInfo.Arguments = arguments; 
      } 

      process.StartInfo.CreateNoWindow = true; 
      process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; 
      process.StartInfo.UseShellExecute = false; 

      process.StartInfo.RedirectStandardError = true; 
      process.StartInfo.RedirectStandardOutput = true; 

      var stdOutput = new StringBuilder(); 
      var errOutput = new StringBuilder(); 
     #endregion 

     #region redirect stdOut/stdError 
      process.OutputDataReceived += (sender, args) => 
      { 
       SendCommmandParser(args.Data); 
       stdOutput.Append(args.Data + "\n"); 
       Debug.WriteLine("S: " + args.Data); 
      }; 

      process.ErrorDataReceived += (sender, args) => 
      { 
       SendCommmandParser(args.Data); 
       errOutput.Append(args.Data + "\n"); 
       // if the form is freezing, the debugger will still output these 
       Debug.WriteLine("E: " + args.Data); 
      }; 
     #endregion 

     #region run process 
      try 
      { 
       process.Start(); 
       process.BeginOutputReadLine(); 
       process.BeginErrorReadLine(); 

       while (!process.HasExited) 
       { 
        System.Windows.Forms.Application.DoEvents(); 

        string command = commandsForMain.Take(); 

        if (command == "..cancel..") 
        { 
         Debug.WriteLine("============== 1"); 
         process.Kill(); 

         while (process != null && !process.HasExited) 
         { 
          //wait 
         } 
         // return if canceled to provide excetion (process == null) 
         return "C"; 
        } 

        if (command == "..break..") 
        { 
         Debug.WriteLine("============== 2"); 
         process.WaitForExit(); 
         break; 
        } 
        /*...*/ 
       } 

       Debug.WriteLine("============== 3"); 
       SendCommmandParser("..break.."); 
      } 
      catch 
      { 
      } 
     #endregion 
      Debug.WriteLine("============== 4"); 


     //... handling OK, CANCEL, ERROR 
    } 

任何人都可以找到一个结构性问题,这会导致这种冻结? (实际上我运行2个转换没有任何错误,但没有改变代码)

感谢您的帮助。

〜ADD〜 现在我得到了一个冻结运行,调试器不会输出"============== #",所以解析线程确实冻结了...但是为什么?

+2

如果你调用'Application.DoEvents()'*和*使用一个线程,你*必须*做错了什么。除非设计出现问题,否则绝对不应该调用'Application.DoEvents()'。所有的工作应该在线程中完成。用户界面应始终保持响应。而且你不能调用'Application.DoEvents()' - 我有提到吗? ;) –

+0

,我该如何重组?如果我在'showParsedConsole()'中取消了'Application.DoEvents()'的注释,那么ParsingForm会冻结。而且工作人员不应该反过来调用ParsingForm,因为表单只是它的工作显示。问题是,工作人员必须等到ffmpeg准备就绪后,我实际上不知道如何重新组织... – gu471

+0

它看起来像我整个'RunExternalExe()'应该从一个单独的线程运行。使用[BackgroundWorker组件](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx)在单独的线程中运行它可能是最简单的。 –

回答

0

感谢马修,真的好像Application.DoEvents()提出了冻结。 我不知道为什么,但我完全重组了代码,所以主gui将转换请求发送到BlockingCollection<>,这将由新线程处理。

为了让窗口解冻,我使用主GUI线程初始化了它,并使用Invoke s的工作线程对其进行了更新。

重组另一个项目很困难,因为主窗体在成功调用不同条件的转换时,实际上它起作用。

而对于未来,我知道,只要调用:紧张的工作与类只有一个调用,使后面的并行化容易;)

相关问题