2011-07-20 7 views
2

私は、System.Diagnostics.Processクラスを使用して、分離されたプロセスでwavファイルをmp3ファイルに変換しています。このような仕事をしていません方法:System.Diagnostics.Processインスタンスをガベージコレクションできますか?

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(); 
    } 

procは唯一のローカル変数であるような方法を返すとき、理論的にはそれはもう存在していないので、私はGC心配です。したがって、procはガベージコレクトされ、コールバック関数の完了は決して呼び出されません。

しかし、私は実際にどこかでprocを記録し、プロセスが終了した後にそれを処分することは望ましくありません。なぜなら、wavからmp3への変換がどのように実装されるかについての内部メカニズムを明らかにするからです。

GCに関する懸念は有効ですか? GCに潜在的な問題がある場合は、この方法で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を心配する必要はありません。

答えて

2

アプリケーションにルートがないオブジェクトは、ガベージコレクションの候補です。コールバックが確実に起きるように、procへの参照を格納する場所を見つける必要があります。そうしないと、未定義の動作が発生します。

あなたのケースでは、procをカプセル化したオブジェクトをパブリックインターフェイス経由で公開せずに返すというオプションがあります。残念なことに、希望の動作が確実に行われるように、基本的な実装の一部をConvertWavToMp3の呼び出し元にリークする必要があります。

+1

Procは他の内部コードによって参照されている内部リソースを使用しているため、GCedには届かないかもしれませんが、プロセスが終了しないように参照をどこかに置いておくべきです。ハンドラが長すぎる(proc参照が必要なくなった場合を除きます) –

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アプリケーションを使用している場合は、プロキシへの参照を保持してください。次のようなもの:

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スレッド上にある必要があり、UIスレッドでExitedイベントが発生しません。うまくいけば、これはあなたが正しい方向に動くようになります。

関連する問題