-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
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.