Может ли экземпляр System.Diagnostics.Process быть сборщиком мусора? - PullRequest
2 голосов
/ 21 июля 2011

Я использую класс 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 является потенциальной проблемой, могу ли я предотвратить ее, не возвращая proc в этом методе?

Кстати, я использую Mono на Linux.

Редактировать

Спасибо за ответы. Я подтвердил, что мне нужно сохранить копию процесса. Итак, вот что я сделал:

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 ]

2 голосов
/ 21 июля 2011

Любой объект без рута в приложении является кандидатом на сборку мусора. Чтобы убедиться, что ваш обратный вызов сработал, вам нужно найти место для хранения ссылки на proc, иначе у вас будет неопределенное поведение.

Одним из вариантов в вашем случае будет возвращение объекта, который инкапсулирует proc, без предоставления его через публичный интерфейс. К сожалению, в вашем случае вы должны передать немного базовой реализации вызывающей стороне ConvertWavToMp3, чтобы гарантировать, что происходит желаемое поведение.

0 голосов
/ 21 июля 2011

Вот альтернативный пример кода, который будет работать. Тем не менее, он заблокирует вызов 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();
    }
}

Как я уже сказал, это, вероятно, не то, что вы хотите, если, например, вы не в консольном приложении. Если вы находитесь в приложении с графическим интерфейсом, сохраните ссылку на ваш процесс. Что-то вроде:

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(...). Если вы собираетесь делать вещи, связанные с пользовательским интерфейсом, они должны быть в потоке пользовательского интерфейса, и это событие Exited не будет запускаться в потоке пользовательского интерфейса. Надеюсь, это заставит вас двигаться в правильном направлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...