c#Process子进程运行不结束(发生死锁)的问题原因及解决方案

1.问题

在项目中需要调用子程序,用Process方式重定向标准输出和错误输出到主进程,创建并启动子进程后发现运行到一定步骤就一直在输出相同信息,像卡住了一样,一直不结束;

而用cmd用相同的调用参数运行子程序发现能够正常运行和结束。

那么是什么原因呢?初步分析在代码中使用Process的方式考虑的不够完善,运行的过程中发生了某种暂时“不明确的行为”。

2.解决思路

2.1.搜索相关关键词

Process方式和直接用cmd调用 两者的调用参数一致,并且都能运行,首先bing/baidu上搜索了相关的关键词,发现有些使用Process的示例博客文章,

初步了解了Process各种参数的意义和用法,虽然没找到和问题直接相关的文章资料,但也算有所收获。

如:https://blog.csdn.net/e295166319/article/details/77932082C# Process方法调用cmd执行命令;

2.2.看Microsoft的帮助文章

 

比如,在光标放在p.StartInfo.RedirectStandardError = true;上按F1,跳出如下文章,

ProcessStartInfo.RedirectStandardError Property

 

建议看英文,中文有时候翻译的很生硬,反而容易理解错误或者不明所以,为了让自己情绪稳定一点还是看原文吧,一般在右上角或者左下角有切换语言的入口!

往下翻,边看边翻,慢点没关系,就怕错过了关键点~

嗯?好像是这里~

2.2.1.场景一


The following example shows how to read from a redirected error stream and wait for the child process to exit. It avoids a deadlock condition by calling p.StandardError.ReadToEnd before p.WaitForExit. A deadlock condition can result if the parent process calls p.WaitForExit before p.StandardError.ReadToEnd and the child process writes enough text to fill the redirected stream. The parent process would wait indefinitely for the child process to exit. The child process would wait indefinitely for the parent to read from the full StandardError stream.


using System;
using System.Diagnostics;

public class Example
{
   public static void Main()
   {
      var p = new Process();  
      p.StartInfo.UseShellExecute = false;  
      p.StartInfo.RedirectStandardError = true;  
      p.StartInfo.FileName = "Write500Lines.exe";  
      p.Start();  

      // To avoid deadlocks, always read the output stream first and then wait.  
      string output = p.StandardError.ReadToEnd();  
      p.WaitForExit();

      Console.WriteLine($"\nError stream: {output}");
   }
}
// The end of the output produced by the example includes the following:
//      Error stream:
//      Successfully wrote 500 lines.

当发生上图中的场景时,主线程和子线程前进一步都显得遥遥无期,发生了死锁。

2.2.2.场景二

There is a similar issue when you read all text from both the standard output and standard error streams. The following C# code, for example, performs a read operation on both streams. It avoids the deadlock condition by performing asynchronous read operations on the StandardError stream. A deadlock condition results if the parent process calls p.StandardOutput.ReadToEnd followed by p.StandardError.ReadToEnd and the child process writes enough text to fill its error stream. The parent process would wait indefinitely for the child process to close its StandardOutput stream. The child process would wait indefinitely for the parent to read from the full StandardError stream.

using System;
using System.Diagnostics;

public class Example
{
   public static void Main()
   {
      var p = new Process();  
      p.StartInfo.UseShellExecute = false;  
      p.StartInfo.RedirectStandardOutput = true;  
      string eOut = null;
      p.StartInfo.RedirectStandardError = true;
      p.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => 
                                 { eOut += e.Data; });
      p.StartInfo.FileName = "Write500Lines.exe";  
      p.Start();  

      // To avoid deadlocks, use an asynchronous read operation on at least one of the streams.  
      p.BeginErrorReadLine();
      string output = p.StandardOutput.ReadToEnd();  
      p.WaitForExit();

      Console.WriteLine($"The last 50 characters in the output stream are:\n'{output.Substring(output.Length - 50)}'");
      Console.WriteLine($"\nError stream: {eOut}");
   }
}
// The example displays the following output:
//      The last 50 characters in the output stream are:
//      ' 49,800.20%
//      Line 500 of 500 written: 49,900.20%
//      '
//
//      Error stream: Successfully wrote 500 lines.

该场景和场景一类似。

2.3.解决方案

避免相互等待而发生死锁的情况,按照帮助文档中的描述,采用异步读取StandardError。

using (Process p = new Process())
{
    p.StartInfo.FileName = "java.exe";
    p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动
    p.StartInfo.RedirectStandardInput = false;  //接受来自调用程序的输入信息
    p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息
    p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出
    p.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
    {
        Console.WriteLine(e.Data);
    });

    p.StartInfo.CreateNoWindow = true;          //不显示程序窗口
    p.StartInfo.Arguments = argumentsFortheJarFile;
    if (!p.Start()) //启动程序
    {
        Console.WriteLine("未能启动程序:" + jarFile);
        return;
    }

    // To avoid deadlocks, use an asynchronous read operation on at least one of the streams.  
    p.BeginErrorReadLine();

    //获取cmd窗口的输出信息
    while (!p.StandardOutput.EndOfStream)
    {
        string oneLine = p.StandardOutput.ReadLine();

        Console.WriteLine(oneLine);
    }
        p.StandardOutput.Close();

        p.WaitForExit();//等待程序执行完退出进程
        p.Close();
}

 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值