C#读取SQLPlus进程输出后程序停滞、线程退出问题求助
此前功能正常,修改内容后出现异常:在C#中通过Process调用SQLPlus进程,能成功创建视图并读取到View created.输出,但随后出现线程以代码0退出的提示,程序卡在Output方法的while循环中无法继续,网站也无法更新。即使不使用依赖注入,在单个类中测试也存在该问题。
相关代码如下:
Program.cs
builder.Services.AddScoped<IExecuter,ShellExecuter>();
ShellExecuter.cs
private List<string> _commands = new List<string>(); private Process _process; private ProcessStartInfo _startInfo; public ShellExecuter(){ _startInfo = new ProcessStartInfo(); _startInfo.WorkingDirectory = Path.GetTempPath(); _startInfo.FileName = "sqlplus.exe"; _startInfo.UseShellExecute = false; _startInfo.RedirectStandardOutput = true; _startInfo.RedirectStandardError = true; _startInfo.RedirectStandardInput = true; _startInfo.CreateNoWindow = true; _process = new Process(); } public void Start() { _startInfo.Arguments = $"-s user/pass@db"; _process.StartInfo = _startInfo; // _process.EnableRaisingEvents = true; _process.Start(); _process.ErrorDataReceived += (sender, args) => { System.Diagnostics.Debug.WriteLine("Error: " + args.Data); }; process.Exited += new System.EventHandler(Exited); } // ...添加命令和写入命令的方法 public string Output() { string line = ""; while (!_process.StandardOutput.EndOfStream) { line += _process.StandardOutput.ReadLine(); System.Diagnostics.Debug.WriteLine("Output: " + line); } }
HomeController.cs
public IActionResult Index(IExecuter exec) { exec.Start(); exec.AddCommand(" create or replace view testview(ID) as select ID from MyUSER;"); exec.Execute(); var output = exec.Output(); return Content(output); }
以下是你忽略的几个关键问题:
未启动错误流异步读取
注册了ErrorDataReceived事件,但没有调用_process.BeginErrorReadLine()启动异步读取。当SQLPlus产生错误输出时,错误流缓冲区会被填满,导致进程阻塞无法正常退出,进而让StandardOutput.EndOfStream始终为false,循环无限卡住。未发送退出命令终止SQLPlus
SQLPlus执行完命令后会停留在交互等待状态,不会自动退出。即便命令执行完成并输出View created.,进程仍然存活,StandardOutput.EndOfStream永远不会触发,导致Output方法的循环无限等待。必须在所有命令执行完成后,通过StandardInput发送exit;命令让SQLPlus正常退出。Output方法未结合进程退出状态判断
当进程已经以代码0退出后,仍在循环等待EndOfStream,此时读取操作可能会阻塞。需要结合_process.HasExited状态来判断,避免无限循环。Exited事件注册存在拼写错误
代码中process.Exited += ...的process应为成员变量_process,否则无法正确注册事件,影响进程退出的事件监听。
修复代码示例
- 修正Start方法,添加错误流读取并修复事件注册:
public void Start() { _startInfo.Arguments = $"-s user/pass@db"; _process.StartInfo = _startInfo; _process.EnableRaisingEvents = true; // 开启事件触发 _process.Start(); _process.ErrorDataReceived += (sender, args) => { if (!string.IsNullOrEmpty(args.Data)) System.Diagnostics.Debug.WriteLine("Error: " + args.Data); }; _process.Exited += Exited; // 修正拼写错误 _process.BeginErrorReadLine(); // 启动错误流异步读取 }
- 在Execute方法末尾添加退出命令:
public void Execute() { foreach (var cmd in _commands) { _process.StandardInput.WriteLine(cmd); } _process.StandardInput.WriteLine("exit;"); // 发送退出命令 _process.StandardInput.Flush(); }
- 优化Output方法,结合进程状态判断:
public string Output() { StringBuilder sb = new StringBuilder(); // 用StringBuilder提升拼接效率 while (!_process.HasExited || !_process.StandardOutput.EndOfStream) { if (_process.StandardOutput.Peek() >= 0) // 检查是否有可读内容 { string line = _process.StandardOutput.ReadLine(); sb.Append(line); System.Diagnostics.Debug.WriteLine("Output: " + line); } else { System.Threading.Thread.Sleep(100); // 短暂等待,避免空循环占用CPU } } return sb.ToString(); }
内容的提问来源于stack exchange,提问作者SuperNev

