2017-07-22 8 views
2

プログラムでいくつかのコマンドを実行しようとしています。そのためにはSystem.Diagnostics.Processを使用しています。私はそれを動作させるように管理しました。コマンドを1回実行すると、戻り値は正しいです。その後、プロセスの実行ごとにタスクを作成してプロセスのスピードを上げようとしましたが、ここで問題が発生しています。ここでマルチタスク環境でのプロセス実行

コマンドを実行するためのクラスです:

class ProcessExec 
{ 
    public string Start(string command) 
    { 
     string res = ""; 

     Process process = new Process(); 
     process.EnableRaisingEvents = true; 
     process.StartInfo.FileName = "powershell.exe"; 
     process.StartInfo.Arguments = command; 
     process.StartInfo.UseShellExecute = false; 
     process.StartInfo.RedirectStandardOutput = true; 
     process.StartInfo.CreateNoWindow = true; 

     process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => 
     { 
      res = res + e.Data; 
     }; 

     process.Start(); 
     process.BeginOutputReadLine(); 
     process.WaitForExit(10000); 

     return res; 
    } 
} 

そして、これは私の主である:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Start"); 

     List<Task> tasks = new List<Task>(); 

     ProcessExec exec = new ProcessExec(); 

     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 

     string res1 = ""; 
     tasks.Add(Task.Run(() => { res1 = exec.Start("date"); })); 
     string res2 = ""; 
     tasks.Add(Task.Run(() => { res2 = exec.Start("hostname"); })); 
     string res3 = ""; 
     tasks.Add(Task.Run(() => { res3 = exec.Start("date"); })); 
     string res4 = ""; 
     tasks.Add(Task.Run(() => { res4 = exec.Start("date"); })); 
     string res5 = ""; 
     tasks.Add(Task.Run(() => { res5 = exec.Start("date"); })); 
     string res6 = ""; 
     tasks.Add(Task.Run(() => { res6 = exec.Start("ipconfig"); })); 
     string res7 = ""; 
     tasks.Add(Task.Run(() => { res7 = exec.Start("date"); })); 
     string res8 = ""; 
     tasks.Add(Task.Run(() => { res8 = exec.Start("date"); })); 

     Task.WaitAll(tasks.ToArray()); 

     sw.Stop(); 

     Console.WriteLine(sw.Elapsed.TotalSeconds); 

     Console.WriteLine("1 - " + res1); 
     Console.WriteLine("2 - " + res2); 
     Console.WriteLine("3 - " + res3); 
     Console.WriteLine("4 - " + res4); 
     Console.WriteLine("5 - " + res5); 
     Console.WriteLine("6 - " + res6); 
     Console.WriteLine("7 - " + res7); 
     Console.WriteLine("8 - " + res8); 

     Console.WriteLine("End"); 
     Console.ReadKey(); 
    } 
} 

これは私の出力である:

Start 
7,4867498 
1 - 22 de julho de 2017 10:25:46  
2 -  
3 - 22 de julho de 2017 10:25:48 
4 - 22 de julho de 2017 10:25:48  
5 -  
6 -  
7 - 22 de julho de 2017 10:25:48 
8 - 22 de julho de 2017 10:25:48 
End 

、私はと思います私の問題はOutputDataReceivedイベントと別のスレッドに関連していますが、私は完全にはわかりません。誰が問題なのか分かっていて、どのように解決できますか?

+0

あなたの 'Start'メソッドはプロセスの実行を待っていません。つまり、asynchronusイベントハンドラで 'process.WaitForExit(10000); 'を使用しますが、結果を返すときにあなたのプロセスはまだ実行中です。したがって、単に 'process.WaitForExit()'を使用してください。詳細については、[MSDNの注釈](https://msdn.microsoft.com/en-us/library/ty0d8k56(v = vs.110).aspx#Anchor_2) –

+0

を参照してください。ああ、また、WaitForExit(Int32)に関するMSDNのドキュメントを読んで、私はそれが何か他のことをしたと思った。お手伝いありがとう。 – lulas

答えて

1

出力が何であるかを説明するだけでなく、出力する内容がの場合、になると良いでしょう。つまり、あなたのコマンドの一部(すべてではありません)が空の出力を持つ理由があなたが求めているようです。そして確かに、それらのコマンドは割り当てられた10秒よりも長い時間がかかるため、出力が読み込まれる前にメソッドが戻ります。

実際に、プロセスが完了するまでメソッドが返されないようにするには、WaitForExit()を使用する理由はありません。私はそれが直感的ではないと思いますが、ここでWaitForExit()を使用しなければならない理由は、プロセス出力を非同期的に消費することを選択したことです。しかし、それを行う理由はありません。プロセスの処理を同期させたいからです。

だから、やってみてください。 Process.StandardOutput.ReadToEnd()に電話をかけると、プロセスが終了するまでブロックされ、すべての出力が返されます。また、メソッドのローカルステート以外のステートを持たないため、クラスが非静的である理由はありません。

だから、このようなものが良いだろう。

static class ProcessExec 
{ 
    public static string Start(string command) 
    { 
     Process process = new Process(); 
     process.StartInfo.FileName = "powershell.exe"; 
     process.StartInfo.Arguments = command; 
     process.StartInfo.UseShellExecute = false; 
     process.StartInfo.RedirectStandardOutput = true; 
     process.StartInfo.CreateNoWindow = true; 

     process.Start(); 

     return process.StandardOutput.ReadToEnd(); 
    } 
} 

私はあなたの実装について変化するであろう他の事は、ローカル変数を使用することです。実行するすべてのコマンドにローカル変数を取り込むのではなく、Taskを使用した場合は、Taskオブジェクト自体のコマンドの結果を表す方が良いでしょう。例えば

static void Main(string[] args) 
{ 
    Console.WriteLine("Start"); 

    List<Task<string>> tasks = new List<Task<string>>(); 

    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 

    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("hostname"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("ipconfig"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 
    tasks.Add(Task.Run(() => ProcessExec.Start("date"))); 

    Task.WaitAll(tasks); 

    sw.Stop(); 

    Console.WriteLine(sw.Elapsed.TotalSeconds); 

    Console.WriteLine(string.Join(Environment.NewLine, 
     tasks.Select((t, i) => $"{i + 1} - {t.Result}"))); 

    Console.WriteLine("End"); 
    Console.ReadKey(); 
} 

その後、あなたは変数を必要としない、それが代わりに個別に各1に名前を付けたのループ変数を使用して、コマンドの結果を取得するのは簡単です。

関連する問題