轉載:http://www.cnblogs.com/KissKnife/archive/2009/09/07/1561629.html
前段時間遇到一個問題,搞得焦頭爛額,現在記錄下來,希望對大家有所幫助。
程序里我使用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();',通過讀取輸出流,以便釋放相應的緩沖。