可以对System.Diagnostics.Process实例进行垃圾回收吗?

时间:2011-07-20 22:51:29

标签: c# mono

我正在使用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是潜在的问题,有没有办法可以阻止它而不必在这种方法中返回proc?

BTW,我在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了。

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

这是一个可行的替代代码示例。但是,它将在进程执行时阻止对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线程上触发。希望这能让你朝着正确的方向前进。