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

.NET读取Git分支时,先等待进程退出为何会卡住?

问题描述

我尝试通过.NET读取Git仓库的分支列表,以下脚本可以正常运行:

open System.Diagnostics

[<RequireQualifiedAccess>]
module Async =

  let parallel2 a b =
    async {
      let! x = Async.StartChild a
      let! y = Async.StartChild b

      let! i = x
      let! j = y

      return i, j
    }

async {
  let fileName = "git"
  let args = [ "-P"; "branch"; "-a" ]

  let psi = ProcessStartInfo(
    FileName = fileName,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true
  )

  for arg in args do
    psi.ArgumentList.Add(arg)

  let p = Process.Start(psi, EnableRaisingEvents = true)

  let! ct = Async.CancellationToken

  let! output, () =
    Async.parallel2
      (
        p.StandardOutput.ReadToEndAsync()
        |> Async.AwaitTask
      )
      (
        p.WaitForExitAsync(ct)
        |> Async.AwaitTask
      )

  if p.ExitCode <> 0 then
    let! error =
      p.StandardError.ReadToEndAsync()
      |> Async.AwaitTask

    return failwith $"{error}"
  else
    return output
}
|> Async.RunSynchronously
|> printfn "%s"

但以下脚本始终无法终止,卡在WaitForExitAsync处:

open System.Diagnostics

async {
  let fileName = "git"
  let args = [ "-P"; "branch"; "-a" ]

  let psi = ProcessStartInfo(
    FileName = fileName,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true
  )

  for arg in args do
    psi.ArgumentList.Add(arg)

  let p = Process.Start(psi, EnableRaisingEvents = true)

  let! ct = Async.CancellationToken

  do!
    p.WaitForExitAsync(ct)
    |> Async.AwaitTask

  if p.ExitCode <> 0 then
    let! error =
      p.StandardError.ReadToEndAsync()
      |> Async.AwaitTask

    return failwith $"{error}"
  else
    let! output =
      p.StandardOutput.ReadToEndAsync()
      |> Async.AwaitTask

    return output
}
|> Async.RunSynchronously
|> printfn "%s"

补充说明:运行环境为Ubuntu上的.NET 6。

原因解释

这是因为标准输出缓冲区已满导致的进程阻塞:

  • 当重定向子进程的标准输出/错误流时,子进程会将输出写入对应缓冲区。如果父进程未及时读取这些缓冲区,一旦缓冲区被填满,子进程会暂停执行,等待父进程读取数据腾出空间。
  • 第二个脚本先调用WaitForExitAsync等待子进程结束,但此时父进程并未读取StandardOutput的内容。Git命令输出的分支信息会填满标准输出缓冲区,导致Git进程卡住无法退出,进而让WaitForExitAsync永远无法完成。
  • 第一个脚本通过parallel2同时启动读取标准输出和等待进程退出的异步任务,进程输出内容时父进程能及时读取缓冲区,不会让子进程因缓冲区满而阻塞,因此可以正常结束。

另外需要注意:如果Git命令同时有大量错误输出,仅读取标准输出也可能导致类似问题,最佳实践是同时异步读取标准输出和标准错误流,避免任一缓冲区满导致进程阻塞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:47:00