【问题标题】:Could a System.Diagnostics.Process instance be garbage collected?System.Diagnostics.Process 实例可以被垃圾收集吗?
【发布时间】:2011-07-20 22:51:28
【问题描述】:

我正在使用 System.Diagnostics.Process 类在单独的进程中将 wav 文件转换为 mp3 文件。完成这项工作的方法是这样的:

    public void ConvertWavToMp3 (TempFile srcFile, string title, Action<TempFile, Exception> complete)
    {
        var argument_fmt = "-S --resample 16 --tt {0} --add-id3v2  {1} {2}";
        var dstFile = new TempFile(Path.GetTempFileName());

        var proc = new System.Diagnostics.Process ();

        proc.EnableRaisingEvents = true;
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.FileName = "lame";
        proc.StartInfo.Arguments = String.Format (argument_fmt, 
                                                  title,
                                                  srcFile.Path,
                                                  dstFile.Path);

        proc.Exited += delegate(object sender, EventArgs e) {
            proc.WaitForExit();
            srcFile.Delete();
            complete(dstFile, null);
        };

        proc.Start();
    }

我很担心 GC,因为 proc 只是一个局部变量,理论上当方法返回时它不再存在。因此,proc可以被垃圾回收,并且永远不会调用回调函数完成。

但我真的不想在某个地方记录 proc 并在进程退出后处理它,因为这会暴露 wav 到 mp3 转换的内部机制。

我对 GC 的担忧是否有效?如果 GC of 是潜在问题,有什么方法可以防止它,而不必在此方法中返回 proc?

顺便说一句,我在 linux 上使用 Mono。

编辑

感谢您的回复。我确认我需要保留该流程的副本。所以这就是我所做的:

public class LameConverter : IAudioConverter
{
    // We need to store a reference to the process in case it was GCed.
    IList<Process> _ProcList = new List<Process>();

    public void ConvertWavToMp3 (TempFile srcFile, string title, Action<TempFile, Exception> complete)
    {
                    // .. skipped ..
        proc.Exited += delegate(object sender, EventArgs e) {
            lock (this) {
                _ProcList.Remove(proc);             
            }
            proc.Dispose();
            srcFile.Delete();
            complete(dstFile, null);
        };

        proc.Start();

        lock (this) {
            _ProcList.Add(proc);
        }
    }
}

只要调用者持有对 LameConverter 的引用,我就不再需要担心 GC。

【问题讨论】:

    标签: c# mono


    【解决方案1】:

    应用程序中没有根的任何对象都是垃圾回收的候选对象。为了确保您的回调触发,您需要找到一些地方来存储对proc 的引用,否则您将有未定义的行为。

    在您的情况下,一个选项是返回一个封装 proc 的对象,而不通过公共接口公开它。不幸的是,在您的情况下,您必须将一些底层实现泄漏给ConvertWavToMp3 的调用者,以确保发生所需的行为。

    【讨论】:

    • 非常好的观察结果,尽管确实发生的一件事是因为 Proc 使用了被其他内部代码引用的内部资源,它可能不会被 GCed,但你说他应该将引用保留在某处是正确的进程退出和处理程序花费的时间太长(除非不再需要 proc 引用)
    【解决方案2】:

    这是一个可行的替代代码示例。但是,它会在进程执行时阻止对 ConvertWavToMp3(...) 的调用。可能不是你想要的。

    public void ConvertWavToMp3 (TempFile srcFile, string title, Action<TempFile, Exception> complete)
    {
        var argument_fmt = "-S --resample 16 --tt {0} --add-id3v2  {1} {2}";
        var dstFile = new TempFile(Path.GetTempFileName());
    
        var proc = new System.Diagnostics.Process ();
    
        proc.EnableRaisingEvents = true;
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.FileName = "lame";
        proc.StartInfo.Arguments = String.Format (argument_fmt, 
                                                  title,
                                                  srcFile.Path,
                                                  dstFile.Path);
    
        using(var wh = new System.Threading.ManualResetEvent(false))
        {
            proc.Exited += delegate(object sender, EventArgs e) {
                proc.WaitForExit();
                srcFile.Delete();
                complete(dstFile, null);
                wh.Set();
            };
    
    
            proc.Start();
            wh.WaitOne();
        }
    }
    

    就像我说的,这可能不是您想要的,除非您使用的是控制台应用程序。如果您使用的是 GUI 应用程序,请保留对您的 proc 的引用。比如:

    public class MyForm : Form
    {
        // other form stuff
    
        private System.Diagnostics.Process _encoderProc;
    
        private void doEncode_Click(object sender, EventArgs e)
        {
            var argument_fmt = "-S --resample 16 --tt {0} --add-id3v2  {1} {2}";
            var dstFile = new TempFile(Path.GetTempFileName());
    
            var proc = new System.Diagnostics.Process ();
    
            proc.EnableRaisingEvents = true;
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.FileName = "lame";
            proc.StartInfo.Arguments = String.Format (argument_fmt, 
                                                      title,
                                                      srcFile.Path,
                                                      dstFile.Path);
    
            proc.Exited += delegate(object sender, EventArgs e) {
                proc.WaitForExit();
                srcFile.Delete();
    
                this.BeginInvoke((MethodInvoker)delegate {
                    // INSERT CODE HERE: your UI-related stuff that you want to do with dstFile
                    this._encoderProc = null;
                });
            };
    
            proc.Start();
            this._encoderProc = proc;
        }
    }
    

    注意BeginInvoke(...) 的使用。如果你要做与 UI 相关的事情,它需要在 UI 线程上,并且 Exited 事件不会在 UI 线程上触发。希望这能让您朝着正确的方向前进。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-23
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多