2017-07-22 66 views
2

我正在執行一些命令,我​​正在使用System.Diagnostics.Process。我設法設置它的工作,當我一一執行命令時,返回是正確的。然後我試圖通過爲每個流程執行創建任務來加速流程,這裏是我遇到問題的地方。在多任務環境中執行進程

這裏是執行命令的類:

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

您的'開始'方法不會等待進程的執行。我的意思是你使用'process.WaitForExit(10000);'與asynchronus事件處理程序,但你的過程仍然在執行,當你返回結果。所以只需使用'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(); 
} 

那麼你不需要的變量,並且很容易使用循環變量,而不必單獨命名每一個檢索命令的結果。