Skip to content

Running a System.Diagnostics.Process with both async stdout/stderr redirection blocks a ThreadPool thread #81896

@drauch

Description

@drauch

Description

If you run a System.Diagnostics.Process and redirect both stdout and stderr in an async way (using the BeginOutputReadLine/BeginErrorReadLine methods) it blocks a thread pool thread.

Reproduction Steps

The following C# interactive notebook:

using System.Diagnostics;
using System.Threading;

bool stop = false;
var threadPoolMonitoringThread = new Thread(() => { while (!stop) { Console.WriteLine($"{DateTime.Now}: ThreadCount: {ThreadPool.ThreadCount}, PendingWorkItemCount: {ThreadPool.PendingWorkItemCount}"); Thread.Sleep(1000); } });
threadPoolMonitoringThread.Start();

var tasks = Enumerable.Range(0, 10).Select(async _ => {
  using (Process process = new Process())
    {
      process.StartInfo.FileName = "pwsh.exe";
      process.StartInfo.Arguments = "-Command Start-Sleep -Seconds 10";
      process.StartInfo.UseShellExecute = false;
      process.StartInfo.CreateNoWindow = true;
      process.StartInfo.RedirectStandardOutput = true;
      process.StartInfo.RedirectStandardError = true;
      
      process.OutputDataReceived += (s, a) => { };
      process.ErrorDataReceived += (s, a) => { };

      process.Start();    

      process.BeginOutputReadLine();      
      process.BeginErrorReadLine();

      await process.WaitForExitAsync();      
    }
});

await Task.WhenAll(tasks);
stop = true;

reproduces the problem. You can immediately see the PendingWorkItemCount raising to very high numbers, while actually all the WaitForExitAsync calls should not block threads on the thread pool at all.

If you either remove the BeginOutputReadLine or the BeginErrorReadLine call the PendingWorkItemCount stays 0 as expected. So it must have something to do with redirecting both streams.

Expected behavior

The thread pool should not be filled by 10 parallel async calls.

Actual behavior

The thread pool is filled up, because each call blocks a thread pool thread.

Regression?

No response

Known Workarounds

No response

Configuration

Reproduced on .NET 6 and .NET 7

Other information

I'm pretty confident that I'm using the Process API correctly, also various other Process-class-Wrappers like MedallionShell or CliWrap are having the same problem.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions