You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,否则无法正确注册事件,影响进程退出的事件监听。

修复代码示例

  1. 修正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(); // 启动错误流异步读取
}
  1. 在Execute方法末尾添加退出命令:
public void Execute()
{
    foreach (var cmd in _commands)
    {
        _process.StandardInput.WriteLine(cmd);
    }
    _process.StandardInput.WriteLine("exit;"); // 发送退出命令
    _process.StandardInput.Flush();
}
  1. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 00:15:58