【问题标题】:Elimininating Process.WaitForExit() / StandardOutput deadlock condition消除 Process.WaitForExit() / StandardOutput 死锁条件
【发布时间】:2016-09-04 02:05:59
【问题描述】:

我已经阅读了有关此deadlock condition 的信息,我很确定这会影响我的代码(如下)。我不明白的是:在过去的 12 年里,这段代码在 Windows Server 2003 (.net 2.0) 上运行得非常好。现在我们一直在尝试将其移至 Windows Server 2012,它总是死锁。

虽然我的 DLL 是为“anyCPU”构建的(仍然针对 .net 2.0),但正在运行的可执行进程绝对是 32 位的,并且从 Server 2003 到 Server 2012 的迁移从 32 位到 64 位操作系统。

我想我知道如何解决该问题,但有谁知道为什么这种行为会从 Server 2003 更改为 Server 2012?

public string DoMyProcess(string filenameAndPath, string arguments)
{
    string stdout="";
    int exitCode = 0;               

    try 
    {               
        ProcessStartInfo procStartInfo = new ProcessStartInfo();
        procStartInfo.FileName = filenameAndPath;
        procStartInfo.CreateNoWindow = true;
        procStartInfo.Arguments = arguments;            
        procStartInfo.RedirectStandardOutput = true;
        procStartInfo.UseShellExecute = false;

        System.Diagnostics.Process theProcess = null;
        try
        {
            theProcess = Process.Start(procStartInfo);
            theProcess.WaitForExit();

            exitCode = theProcess.ExitCode;

            // moving this ABOVE WaitForExit should eliminate deadlocks
            // But why did it always work on Server 2003 but not on Server 2012?
            stdout = theProcess.StandardOutput.ReadToEnd();

        }
        catch (System.Exception e)
        {
            string errMsg = e.Message;
            log_the_error("threw an exception: " + e.Message);
        }                   
    }           

    return stdout;
}

更新:

即使按照建议更改上述代码后,仍然存在神秘死锁:

        try
        {
            theProcess = Process.Start(procStartInfo);
            stdout = theProcess.StandardOutput.ReadToEnd();                     
        }
        catch (System.Exception e)
        {
            string errMsg = e.Message;
            log_the_error("threw an exception: " + e.Message);
        }                   
    }           

还有哪些其他条件可能导致该死锁?如果我要检查 StandardError,它会发现任何有用的信息吗?

更新 #2:

FWIW,我们提供了另一个运行 IIS 6 的 Windows Server 2003(32 位)。这是此代码运行了 12 年的原始机器配置(只是偶尔出现死锁)。我们在 Server 2012 IIS 8 上死锁的相同代码在此 Server 2003 上不会死锁。

我们现在拥有自己的最小且完整的代码来重现该问题。但是,我们已获得许可并由该进程执行的 .exe 具有阻止我们发布的保密条款。我意识到这对这里的专家没有帮助。

我们遇到的一个提示是,当通过安装在实际服务器上的 Visual Studio 2013 调试器运行时,进程不会死锁/挂起,而从服务器外部的浏览器调用进程会发生死锁/挂起。奇怪的是——从 2012 年服务器上的浏览器我们无法连接到该测试页面——浏览器只是说“正在连接”并最终超时(但是,可以访问由同一服务器/同一 IIS 8 托管的其他站点来自服务器上的浏览器!)

由于从管理员命令 shell 或非管理员命令 shell 手动运行相同的命令行参数可以完美运行,很难相信这个 32 位可执行文件是 64 位/WOW64 问题,或者它是必需的 DLL。我们继续搜索我们的权限可能导致问题的地方(该进程需要写入一个临时文件夹,我们目前已将其放置在 c:\temp)。

【问题讨论】:

  • 我知道错误的代码模式在互联网上很普遍,但请尝试使用 MS 为Process.OutputDataReceived Event 提供的示例作为起点。请注意,该事件和其他进程事件到达辅助线程,因此请相应地进行计划。
  • 我有几个不同的 SO 答案,虽然回答的问题与您在这里的不同,但确实包括重定向 StandardOutputStandardError 而不会导致代码死锁的示例。您可能会发现其中一个或多个有用:stackoverflow.com/a/33508142stackoverflow.com/a/26722542stackoverflow.com/a/38881345
  • 为了澄清我之前的评论:“阅读 StandardError 可能会或可能不会显示任何有用的东西”。我的意思是外部进程可能已经向StandardError 写入了一些有用的信息,如果您阅读并显示这些信息,它可能会为您提供有关进程未退出的原因的信息。或者它可能不会。不可能从我坐在哪里说。 :)
  • 抱歉,我对 IIS 的了解不够,无法解决任何特定的问题。我不认为这种情况下应该有任何不寻常的东西会改变基本的诊断方法,但我不能确定。我建议从基础开始:你知道死锁意味着两个不同的执行线程正在相互等待;所以,首先要确定什么在等待什么,小心区分真正的等待和需要很长时间的东西。
  • 对不起,没有。该版本至少需要 VS2010/.NET 4.0 的任务并行库功能,即使那样你也不会得到async/await(仅在 C# 5 和更高版本中)。如果您被困在 .NET 2.0 上,您需要按照我的第一个链接 (stackoverflow.com/a/33508142) 中的示例进行操作。请注意,该示例不完整;您还需要查看问题中的原始代码以获得完整的图片。当然,不需要复制他们的Console.SetOut()等。重要的部分是Output...ErrorDataReceived事件;您将根据自己的需要处理它们。

标签: c# .net visual-studio-2012 process deadlock


【解决方案1】:

没有好的Minimal, Complete, and Verifiable code example,是不可能完整回答的。

我可以告诉你的是,你的代码总是被破坏,并且总是有死锁的可能性。在进程退出之前,您无法从进程中读取任何内容,但如果进程向 stdout 写入的数据过多以至于缓冲区填满并阻塞进程,则进程可能无法退出。

如果您没有重新编译任何东西,但发现您现在看到了死锁,而以前没有,那么最可能的解释是您开始的进程向 stdout 写入的内容比以前更多。 IE。以前所有的输出都可以放入缓冲区,但现在不行了。 (我想在较新的操作系统中缓冲区大小也可能会减少,但这对我来说似乎不太可能。)

您应该继续将呼叫转移到ReadToEnd()。实际上,您应该完全取消 WaitForExit()。如果您正在调用ReadToEnd(),则在进程实际上退出之前不会完成,因此之后调用WaitForExit() 将毫无意义。

【讨论】:

  • 我相信你是对的。该代码是来自一家经验丰富的供应商的示例的一部分,他们应该已经掌握了这一点——我们的工程师当时是 .net 的新手。
猜你喜欢
  • 2013-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多