Tips&Tricks系列五:Process.WaitForExit()与死锁
2009-09-07 10:06
344 查看
前段时间遇到一个问题,搞得焦头烂额,现在记录下来,希望对大家有所帮助。
程序里我使用Process类启动命令行,执行批处理文件 'Create.cmd'(当我手工将此文件拖入命令行执行时,一切正常)。C#程序代码类似如下,其中batchFilePath变量为批处理文件全路径:
批处理文件'Create.cmd'调用'sqlplus'来执行若干个sql文件:
//===================================================
echo Tables on level 0:
if exist InstallScripts\Create01.sql (
echo bas
sqlplus %1/%2@%3 @InstallScripts\Create01.sql | %HideSQLPlusRows%
REM > Logs\Create_%1.txt
)
if exist InstallScripts\Create02.sql (
......
//===================================================
出现的问题是程序运行到'm_BasicDataProc.WaitForExit();'这一行时就阴塞不动.
搞了两天,最后发现原因是出现了死锁。由于标准输出流被重定向,而Process.StandardOutput的缓冲大小是有限制的(据说是4k),所以当缓冲满了的时候(执行上面的批处理文件有很多的输出),子进程(cmd.exe)会等待主进程(C# App)读取并释放此缓冲,而主进程由于调用了WaitForExit()方法,则会一进等待子进程退出,最后形成死锁。
了解了原因后,有3种方法可以解决问题:
1)修改批处理文件,在调用sqlplus时将输出指定到一个log文件,这样被生定向到StandardOutput中的内容相对就少,不容易造成问题:
//===================================================
echo Tables on level 0:
if exist InstallScripts\Create01.sql (
echo bas
sqlplus %1/%2@%3 @InstallScripts\Create01.sql | %HideSQLPlusRows% > Logs\Create_%1.txt
)
......
//===================================================
2)修改C#代码,将'm_BasicDataProc.StartInfo.RedirectStandardOutput = false;',这样所有的输出会在命令行屏幕上直接输出,不会重定向到标准输出流中。
3)修改C#代码,在'm_BasicDataProc.WaitForExit();'前添加'm_BasicDataProc.BeginOutputReadLine();' 或 'm_BasicDataProc.StandardOutput.ReadToEnd();',通过读取输出流,以便释放相应的缓冲。
程序里我使用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 InstallScripts\Create01.sql (
echo bas
sqlplus %1/%2@%3 @InstallScripts\Create01.sql | %HideSQLPlusRows%
REM > Logs\Create_%1.txt
)
if exist InstallScripts\Create02.sql (
......
//===================================================
出现的问题是程序运行到'm_BasicDataProc.WaitForExit();'这一行时就阴塞不动.
搞了两天,最后发现原因是出现了死锁。由于标准输出流被重定向,而Process.StandardOutput的缓冲大小是有限制的(据说是4k),所以当缓冲满了的时候(执行上面的批处理文件有很多的输出),子进程(cmd.exe)会等待主进程(C# App)读取并释放此缓冲,而主进程由于调用了WaitForExit()方法,则会一进等待子进程退出,最后形成死锁。
了解了原因后,有3种方法可以解决问题:
1)修改批处理文件,在调用sqlplus时将输出指定到一个log文件,这样被生定向到StandardOutput中的内容相对就少,不容易造成问题:
//===================================================
echo Tables on level 0:
if exist InstallScripts\Create01.sql (
echo bas
sqlplus %1/%2@%3 @InstallScripts\Create01.sql | %HideSQLPlusRows% > Logs\Create_%1.txt
)
......
//===================================================
2)修改C#代码,将'm_BasicDataProc.StartInfo.RedirectStandardOutput = false;',这样所有的输出会在命令行屏幕上直接输出,不会重定向到标准输出流中。
3)修改C#代码,在'm_BasicDataProc.WaitForExit();'前添加'm_BasicDataProc.BeginOutputReadLine();' 或 'm_BasicDataProc.StandardOutput.ReadToEnd();',通过读取输出流,以便释放相应的缓冲。
相关文章推荐
- C#执行ADD命令是Process.WaitForExit()出现死锁
- C# Process.WaitForExit()与死锁
- C# Process.WaitForExit()与死锁
- Tips&Tricks系列六:ADO.NET OracleConnection.ClearPool(conn)
- 文章目录:ASP.NET AJAX Advance Tips & Tricks 系列文章【共10篇】
- Tips&Tricks系列四:C#面试笔试小贴士
- Tutorial: The best tips & tricks for bash, explained | bash 入门| 极好的bash技艺
- Tips&Tricks系列一:更改VS2005设置
- GetExitCodeThread(hThrd, &exitCode);WaitForSingleObject(hThrd,INFINITE);
- 如何调用一个程序,等待运行结束,并知晓程序运行成功与否:ShellExecuteEx;WaitForSingleObject;GetExitCodeProcess(C代码)
- Tips&Tricks系列二:找不到Asp.net模板?
- 文章目录:ASP.NET AJAX Advance Tips & Tricks 系列文章【共10篇】
- Tips&Tricks系列九:数据库外键的两个细节
- Tips&Tricks系列三:无法在Web服务器上启动调试
- Tips&Tricks系列八:Fail to convert .vsmid,.testrunconfig
- Process.WaitForExit 方法
- Shiny tips & tricks for improving your apps and solving common problems
- 如何调用一个程序,等待运行结束,并知晓程序运行成功与否:ShellExecuteEx;WaitForSingleObject;GetExitCodeProcess(C代码)
- 46 Tips & Tricks for 2D mobile Performance in Unity
- Tips&Tricks系列七:ContextSwitchDeadlock is detected