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

C# Process.Start启用输出流重定向时进程不退出问题解决

问题根因

这是重定向子进程标准流时非常典型的管道缓冲区死锁问题:

  • Windows系统中父子进程间的标准输出/错误管道有固定大小的缓冲区(通常为4KB),当缓冲区被写满后,子进程写入流的操作会被阻塞,直到父进程读取缓冲区内容腾出空间。
  • 你的代码只开启了流重定向,没有任何读取两个标准流的逻辑,yt-dlp运行时会输出大量JSON内容到stdout,很快就会把缓冲区写满,之后子进程就会卡在写入操作上无法继续执行,自然不会触发退出逻辑,HandleProcessExit回调永远不会执行。
  • 关闭流重定向时,子进程输出直接丢弃/写入控制台,不存在缓冲区阻塞问题,所以进程可以正常退出,和你观察到的现象完全一致,和yt-dlp工具本身无关。
修复方案

使用异步流读取的方式持续消费两个标准流的内容,避免缓冲区被写满,这是官方推荐的最稳妥方案,修复后的完整实现代码如下:

var psi = new ProcessStartInfo("yt-dlp.exe"){
    ErrorDialog = false,
    CreateNoWindow = true,
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    // 重定向流必须显式设置该值为false,避免不同.NET版本默认值差异导致异常
    UseShellExecute = false,
    ArgumentList = {
        "--prefer-insecure",
        "--no-check-certificates",
        "--dump-json",
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    },
};

var process = new Process(psi);
// 提前开启退出事件触发,避免进程启动过快导致事件漏触发
process.EnableRaisingEvents = true;

// 用于缓存两个流的输出内容,可根据业务替换为实时处理逻辑
StringBuilder stdoutCache = new StringBuilder();
StringBuilder stderrCache = new StringBuilder();

// 注册标准输出异步接收回调
process.OutputDataReceived += (_, args) =>
{
    if (args.Data != null)
    {
        stdoutCache.AppendLine(args.Data);
        // 此处可添加输出内容的实时处理逻辑
    }
};

// 注册标准错误异步接收回调
process.ErrorDataReceived += (_, args) =>
{
    if (args.Data != null)
    {
        stderrCache.AppendLine(args.Data);
        // 此处可添加错误日志的实时处理逻辑
    }
};

process.Exited += HandleProcessExit;

process.Start();
// 进程启动后立刻开启两个流的异步读取,这一步不能省略
process.BeginOutputReadLine();
process.BeginErrorReadLine();

// 等待进程执行完成
process.WaitForExit();
// .NET Framework 等旧版本框架需要额外调用一次WaitForExit,确保异步读取的剩余内容全部处理完成
// .NET Core 3.0+ 版本可省略该步
process.WaitForExit();

// 执行到此处时即可从stdoutCache、stderrCache中拿到完整的输出内容
避坑说明
  • 不要在单线程中按顺序同步调用process.StandardOutput.ReadToEnd()和process.StandardError.ReadToEnd(),这种写法同样会触发死锁:如果先读stdout,而子进程先把stderr缓冲区写满,子进程会阻塞在stderr写入上,父进程会一直等stdout读完,两边互相等待就形成死锁。
  • 如果确实需要用同步方式读取,必须将两个流的读取操作放到两个独立的线程/Task中并行执行,保证两个流都有持续的消费方,不会出现缓冲区写满阻塞的情况。
  • 所有事件绑定、流配置尽量放在Process.Start()调用之前完成,BeginOutputReadLine/BeginErrorReadLine要在启动后第一时间调用,避免进程启动初期的输出打满缓冲区。

内容的提问来源于stack exchange,提问作者zbigniew flet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:18:21