Using (Process process = new Process())
    process.StartInfo = new ProcessStartInfo(executablePath, args);
    if (workingFolder != null)
         process.StartInfo.WorkingDirectory = workingFolder;
     process.StartInfo.CreateNoWindow = true;
     process.StartInfo.UseShellExecute = false;
     process.StartInfo.RedirectStandardOutput = true;
     process.StartInfo.RedirectStandardError = true;
     DateTime start = DateTime.Now;
     process.Start();
     process.WaitForExit();
     DateTime finish = DateTime.Now;
     string processOutput = process.StandardError.ReadToEnd() + 
     process.StandardOutput.ReadToEnd();

这段代码的作用是调用一个外部程序向一个服务器上传一串数据,原来数据大小是128bit, 现在是4K bit。

尝试了各种方式,在网上搜集各类资料,查找可能卡顿的地方,本来以为是调用的外部程序的问题,最后发现是主程序的问题。

原因分析:

1. 使用同步方式读取执行结果,在StandardOutput.ReadToEnd之前调用了WaitforExit,导致死锁。

原代码使用了同步读取执行结果的方式,在StandardOutput.ReadToEnd之前调用了WaitforExit。当调用方从子进程的重定向流中进行读取时,它依赖于子进程。 调用方等待读取操作,直到子级写入流或关闭流为止。 当子进程写入足够的数据以填充其重定向流时,它依赖于父进程。 子进程将在下一次写入操作之前等待,直到父进程从整个流中读取或关闭流为止。 当调用方和子进程等待彼此完成操作时,会发生死锁条件,并且这两个情况都无法继续。 

简单点说就是当数据量较大时,子进程写入的数据占满了重定向流,此时子进程会停止写入,等待父进程从流读取数据,然而父进程会等待直到子进程完成写入流,此时父进程子进程相互等待,程序陷入死锁。

正确的方法应该是在WaitForExit之前调用StandardOutput.ReadToEnd。

原代码父进程在调用StandardOutput.ReadToEnd之前调用WaitforExit,并且子进程写入足够多的文本以填充重定向的流,则会导致死锁情况。 父进程会无限期地等待子进程退出。 子进程会无限期地等待父进程从整个流中读取StandardOutput 。

2. 同时调用StandardError.ReadToEnd 和 StandardOutput.ReadToEnd.

和上面的原因同理,如果父进程调用 StandardOutput.ReadToEnd 后跟 StandardError.ReadToEnd ,并且子进程写入足够的文本来填充其错误流,则会导致死锁情况。 父进程会无限期地等待子进程关闭其 StandardOutput流。 子进程会无限期地等待父进程从整个流中读取 StandardError。

解决方案:

使用异步方式读取执行结果和错误信息,同时加入延时检查。

示例代码为异步读取数据的方法:

using (Process process = new Process())
    StringBuilder processOutputBuilder = new StringBuilder();
    process.StartInfo = new ProcessStartInfo(executablePath, args);
    if (workingFolder != null)
         process.StartInfo.WorkingDirectory = workingFolder;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.EnableRaisingEvents = true;
    process.OutputDataReceived += (sender, eventArgs) =>
        if (eventArgs.Data != null)
            processOutputBuilder.AppendLine(eventArgs.Data);
            outputWaitHandle.Set();
     process.ErrorDataReceived += (sender, eventArgs) =>
         if (eventArgs.Data != null)
             processOutputBuilder.AppendLine(eventArgs.Data);
             errorWaitHandle.Set();
      process.Start();
      process.BeginOutputReadLine();
      process.BeginErrorReadLine();    
      outputWaitHandle.WaitOne();
      errorWaitHandle.WaitOne();
      process.CancelOutputRead();
      process.CancelErrorRead();

参考链接:

ProcessStartInfo hanging on “WaitForExit”? Why?

MSDN: Process.StandardOutput Property

最近做项目发现创建Process调用外部程序时, 当处理的数据量变大后,Process无法退出,主程序卡死。原代码:Using (Process process = new Process()){ process.StartInfo = new ProcessStartInfo(executablePath, args); if (workingFolder != null) { process.StartInfo.WorkingDirectory = // 摘要: // 启动由包含进程启动信息(例如,要启动的进程的文件名)的参数指定的进程资源,并将该资源与新的 System.Diagnostics.Process // 组件关联。 // 参数: // startInfo: // System.Diagnostics.ProcessStartInfo,包含用于启动进程的信息(包括文件名和任何命令行参数)。
今天写一个C#程序,想调用CMD命令,并读取CMD的输出显示在文本框里,结果出现"卡"现象,手动结束控制台程序就好了。逐步调试发现程序一直在StandardOutput.ReadToEnd();这里停止。 于是我在命令行发送输入信息后面加了一句StandardInput.Close();关闭输入然后再读取就好了 ```csharp 在这里插入代码片 ```public string 命令行_编...
最近做一个编程,用C#调用类似ssh,ftp,runas,adb shell之类有中间输入,中间输出的.网上说使用Process.StartInfo,Process.StandardInput,Process.StandOutput之类的,但是每次都在StandardOutput.Read/ReadToEnd卡,原因复杂,最主要原因是微乳没有做好.下面是个分析 http://blog.163.com/boyinfo@126/blog/static/171646064201111663246435/ 看到蛋都碎了,明白了怎么回事,但不能解决问题. 网上还有很多多线程,不用Error重定向之类的,都不好用.几十行代码,只为获取几行输出?太坑爹了. 我突然发现一个非常简捷的方法.想想网上翻了好久,都找不到,有点生气.那就搞个5分.
在项目中需要调用子程序,用Process方式重定向标准输出和错误输出到主进程,创建并启动子进程后发现运行到一定步骤就一直在输出相同信息,像卡住了一样,一直不结束; 而用cmd用相同的调用参数运行子程序发现能够正常运行和结束。 那么是什么原因呢?初步分析在代码中使用Process的方式考虑的不够完善,运行的过程中发生了某种暂时“不明确的行为”。 2.解决思路 2.1.搜索相关关键词 Process方式和直接用cmd调用 两者的调用参数一致,并且都能运行,首先bing/baidu上搜索了相  Process p;//实例化一个Process对象  p=Process.Start(@"E:\1.txt");//要开启的进程(或 要启用的程序),括号内为绝对路径  p.Kill();//结束进程 //查... 大家都知道,当我们用C#来开发客户端程序的时候,总会不可避免的需要调用外部程序或者访问网站,本篇博客介绍了三种调用外部应用的方法,供参考,下面话不多说了,来一起看看详细的介绍吧。 第一种是利用shell32.dll,实现ShellExecute方法,该方法可同时打开本地程序、文件夹或者访问网站,只要直接输入路径字符串即可, 如C:\Users\Desktop\xx.exe或者https://cn.bing.com/,可以根据返回值判断是否调用成功 (成功0x00000002a , 失败0x00000002) Window wnd = Window.GetWindow(this
一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果
1.c#进程类用Process(在命名空间System.Diagnostics中)定义:使用Process添加using System.Diagnostics(VS自动导入命名空间的方法,将光标方法该类后,三击Shift+Alt+F10) Process可以管理本地计算机的进程的开启关闭: 比如Process.Start(“calc”);可以打开本地的计算器: 也可以通过指定的进程打开一个文件:定义一个ProcessStartInfo的对象psi并在new后面传递文件地址,将p赋值给p.StartInf
今天有个需求,在一个c#写的程序中,需要开启两个unity打包的exe,结果调用的时候遇到了一个问题,unity打包的exe双击打开时没有问题,但是被调用时就会出现问题,后来发现原因是需要调用的exe有配置文件或者启动时需读取其他文件时,请配置一下StartInfo的WorkingDirectory属性为你的应用程序目录。 ProcessStartInfo psf = new Proc...