• C# Process.WaitForExit()与死锁


    前段时间遇到一个问题,搞得焦头烂额,现在记录下来,希望对大家有所帮助。

    程序里我使用Process类启动命令行,执行批处理文件 'Create.cmd'(当我手工将此文件拖入命令行执行时,一切正常)。C#程序代码类似如下,其中batchFilePath变量为批处理文件全路径:

    复制代码
    m_BasicDataProc = new Process();
    m_BasicDataProc.StartInfo.FileName = "cmd.exe";
    m_BasicDataProc.StartInfo.CreateNoWindow = false;
    m_BasicDataProc.StartInfo.UseShellExecute = false;
    m_BasicDataProc.StartInfo.RedirectStandardOutput = true;
    m_BasicDataProc.StartInfo.RedirectStandardInput = true;
    m_BasicDataProc.StartInfo.WorkingDirectory = Path.GetDirectoryName(batchFilePath);
    m_BasicDataProc.Start();
    string batchFileName = Path.GetFileName(batchFilePath);
    StreamWriter inputStream = m_BasicDataProc.StandardInput;
    inputStream.WriteLine(batchFileName);
    inputStream.Close();
    m_BasicDataProc.WaitForExit();
    m_BasicDataProc.EnableRaisingEvents = true;
    复制代码

    批处理文件'Create.cmd'调用'sqlplus'来执行若干个sql文件:
    //===================================================
    echo Tables on level 0:
    if exist InstallScriptsCreate01.sql (
     echo bas
     sqlplus %1/%2@%3 @InstallScriptsCreate01.sql | %HideSQLPlusRows%
     REM > LogsCreate_%1.txt
    )
    if exist InstallScriptsCreate02.sql (
    ......
    //===================================================

    出现的问题是程序运行到'm_BasicDataProc.WaitForExit();'这一行时就阴塞不动.

    搞了两天,最后发现原因是出现了死锁。由于标准输出流被重定向,而Process.StandardOutput的缓冲大小是有限制的(据说是 4k),所以当缓冲满了的时候(执行上面的批处理文件有很多的输出),子进程(cmd.exe)会等待主进程(C# App)读取并释放此缓冲,而主进程由于调用了WaitForExit()方法,则会一进等待子进程退出,最后形成死锁。

    了解了原因后,有3种方法可以解决问题:
    1)修改批处理文件,在调用sqlplus时将输出指定到一个log文件,这样被生定向到StandardOutput中的内容相对就少,不容易造成问题:
    //===================================================
    echo Tables on level 0:
    if exist InstallScriptsCreate01.sql (
     echo bas
     sqlplus %1/%2@%3 @InstallScriptsCreate01.sql | %HideSQLPlusRows% > LogsCreate_%1.txt
    )
    ......
    //===================================================

    2)修改C#代码,将'm_BasicDataProc.StartInfo.RedirectStandardOutput = false;',这样所有的输出会在命令行屏幕上直接输出,不会重定向到标准输出流中。

    3)修改C#代码,在'm_BasicDataProc.WaitForExit();'前添加 'm_BasicDataProc.BeginOutputReadLine();' 或 'm_BasicDataProc.StandardOutput.ReadToEnd();',通过读取输出流,以便释放相应的缓冲。

  • 相关阅读:
    关于sqlserver2008 bcp根据数据表导出xml格式文件的小记
    关于SQL SERVER 2008 X64版本报错:消息 7302,级别 16,无法创建链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.ACE.OLEDB.12.0" 的实例。
    Response.Redirect(),Server.Transfer(),Server.Execute()的区别[转]
    ASP.net应用程序中如何调用客户端的Javascript脚本小结(转)
    重写ListView控件,实现点击列头排序的功能
    关于Response.redirect和Response.End出现线程中止异常的处理(转)
    持续集成cruisecontrol.net学习总结
    [转]关于PowerDesigner反向工程SQL Server2000数据库时生成注释的解决方法
    敏捷开发scrum学习笔记一
    asp.net缓存机制总结(转)
  • 原文地址:https://www.cnblogs.com/tdskee/p/5442955.html
Copyright © 2020-2023  润新知