场景: 点击打开链接
论坛里的朋友没有说到要点, 后来在 这篇文章 得到启发, 然后看 msdn ,
原来还是 exe 输出的内容太多, 导致产生了死锁。
直接贴 msdn 相关内容吧:
ProcessStartInfo.RedirectStandardOutput 属性
获取或设置一个值,该值指示是否将应用程序的输出写入 Process.StandardOutput 流中。
命名空间:System.Diagnostics
程序集:System(在 system.dll 中)
/** @property */ public boolean get_RedirectStandardOutput () /** @property */ public void set_RedirectStandardOutput (boolean value)
public function get RedirectStandardOutput () : boolean public function set RedirectStandardOutput (value : boolean)
属性值
若要将输出写入 Process.StandardOutput 中,则为 true;否则为 false。当 Process 将文本写入其标准流中时,通常将在控制台上显示该文本。通过重定向 StandardOutput 流,可以操作或取消进程的输出。例如,您可以筛选文本、用不同方式将其格式化,也可以将输出同时写入控制台和指定的日志文件中。
注意 |
---|
如果要将 RedirectStandardOutput 设置为 true,必须先将 UseShellExecute 设置为 false。否则,读取 StandardOutput 流时将引发异常。 |
可以同步或异步读取重定向的 StandardOutput 流。Read、ReadLine 和 ReadToEnd 等方法对进程的输出流执行同步读取操作。这些同步读取操作只有在关联的 Process 写入其 StandardOutput 流或关闭该流后才能完成。
相反,BeginOutputReadLine 在 StandardOutput 流上开始异步读取操作。此方法为流输出启用一个指定的事件处理程序并立即返回到调用方,这样当流输出被定向到该事件处理程序时,调用方可以执行其他操作。
同步读取操作在读取 StandardOutput 流的调用方及写入该流中的子进程之间引入一个依赖项。这些依赖项可能导致产生死锁情况。调用方读取子进程的重定向流时依赖于该子进程。调用方将等待读取操作,直到子进程写入流或关闭流为止。子进程写入足够多的数据以填充重定向流的时间依赖于父进程。子进程将等待下一次写操作,直到父进程读取了全部流或关闭该流为止。当调用方和子进程相互等待对方完成操作时,就会产生死锁情况,使双方都无法继续执行操作。您可以通过计算调用方和子进程之间的依赖项从而避免出现死锁情况。
例如,下面的 C# 代码演示如何读取重定向流并等待子进程退出。
// Start the child process. Process p = new Process(); // Redirect the output stream of the child process. p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = "Write500Lines.exe"; p.Start(); // Do not wait for the child process to exit before // reading to the end of its redirected stream. // p.WaitForExit(); // Read the output stream first and then wait. string output = p.StandardOutput.ReadToEnd(); p.WaitForExit();
该代码示例通过在 p.WaitForExit 之前调用 p.StandardOutput.ReadToEnd 避免产生死锁情况。如果父进程在 p.StandardOutput.ReadToEnd 之前调用 p.WaitForExit,并且子进程写入足够多的文本以填充重定向流,就会产生死锁情况。父进程将无限期地等待子进程退出。子进程将无限期地等待父进程读取全部 StandardOutput 流。
在读取标准输出和标准错误流的所有文本时,会出现类似的问题。例如,下面的 C# 代码对这两种流执行读取操作。
// Do not perform a synchronous read to the end of both // redirected streams. // string output = p.StandardOutput.ReadToEnd(); // string error = p.StandardError.ReadToEnd(); // p.WaitForExit(); // Use asynchronous read operations on at least one of the streams. p.BeginOutputReadLine(); string error = p.StandardError.ReadToEnd(); p.WaitForExit();
此代码示例通过对 StandardOutput 流执行异步读取操作避免产生死锁情况。如果父进程在调用 p.StandardOutput.ReadToEnd 之后调用 p.StandardError.ReadToEnd,并且子进程写入足够多的文本以填充错误流,就会产生死锁情况。父进程将无限期地等待子进程关闭其 StandardOutput 流。子进程将无限期地等待父进程读取全部 StandardError 流。
您可以使用异步读取操作避免出现这些依赖项及其潜在的死锁情况。或者,您还可以通过创建两个线程并读取每个线程中每个流的输出来避免产生死锁情况。
Process compiler = new Process(); compiler.StartInfo.FileName = "csc.exe"; compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs"; compiler.StartInfo.UseShellExecute = false; compiler.StartInfo.RedirectStandardOutput = true; compiler.Start(); Console.WriteLine(compiler.StandardOutput.ReadToEnd()); compiler.WaitForExit();
本人代码(参数: needToOutputInfo 在调用时为 false 就不会有任何问题了)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TimingService.Util
{
public class ExecResult
{
public bool Success { get; set; }
public string OutPutString { get; set; }
public string ErrMsg { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Management.Automation;
namespace TimingService.Util
{
public class ExecHelper
{
/// <summary>
/// 执行批处理文件
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static ExecResult ExecBat(string filePath, bool needToOutputInfo)
{
ExecResult r = new ExecResult();
r.Success = true;
try
{
using (Process pro = new Process())
{
FileInfo file = new FileInfo(filePath);
pro.StartInfo.WorkingDirectory = file.Directory.FullName;
pro.StartInfo.FileName = filePath;
pro.StartInfo.CreateNoWindow = true;
pro.StartInfo.RedirectStandardOutput = needToOutputInfo;
pro.StartInfo.RedirectStandardError = needToOutputInfo;
pro.StartInfo.UseShellExecute = false;
pro.Start();
if (needToOutputInfo)
{
r.OutPutString = pro.StandardOutput.ReadToEnd() + pro.StandardError.ReadToEnd();
}
pro.WaitForExit();
}
}
catch (Exception ex)
{
r.Success = false;
r.ErrMsg = ex.Message;
}
return r;
}
public static ExecResult ExecExe(string filePath, bool needToOutputInfo)
{
ExecResult r = new ExecResult();
r.Success = true;
try
{
using (Process pro = new Process())
{
FileInfo file = new FileInfo(filePath);
pro.StartInfo.WorkingDirectory = file.Directory.FullName;
pro.StartInfo.FileName = filePath;
pro.StartInfo.CreateNoWindow = true;
pro.StartInfo.RedirectStandardOutput = needToOutputInfo;
pro.StartInfo.RedirectStandardError = needToOutputInfo;
pro.StartInfo.UseShellExecute = false;
pro.Start();
if (needToOutputInfo)
{
r.OutPutString = pro.StandardOutput.ReadToEnd() + pro.StandardError.ReadToEnd();
}
pro.WaitForExit();
}
}
catch (Exception ex)
{
r.Success = false;
r.ErrMsg = ex.Message;
}
return r;
}
public static ExecResult ExecPowerShell(string filePath, bool needToOutputInfo)
{
ExecResult r = new ExecResult();
r.Success = true;
try
{
string scriptText = File.ReadAllText(filePath);
string outputString = RunScript(scriptText);
if (needToOutputInfo)
{
r.OutPutString = outputString;
}
}
catch (Exception ex)
{
r.Success = false;
r.ErrMsg = ex.Message;
}
return r;
}
private static string RunScript(string scriptText)
{
Collection<PSObject> results = null;
// create Powershell runspace
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
pipeline.Commands.Add("Out-String");
// execute the script
results = pipeline.Invoke();
// close the runspace
runspace.Close();
}
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
public static void RunScript(List<string> scripts)
{
try
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
foreach (var scr in scripts)
{
pipeline.Commands.AddScript(scr);
}
//返回结果
var results = pipeline.Invoke();
runspace.Close();
}
catch (Exception e)
{
Console.WriteLine(DateTime.Now.ToString() + "日志记录:执行ps命令异常:" + e.Message);
}
}
}
}