.NETのアプリケーション開発において、外部プロセスを制御するためのSystem.Diagnostics.Processクラスは非常に重要な役割を担ってきました。

2026年5月、マイクロソフトは.NET 11において、このプロセスAPIに対する数年ぶりの大規模なアップデートを発表しました。

今回の更新では、長年開発者を悩ませてきた標準出力のデッドロック問題が解消されたほか、パフォーマンスが劇的に向上しています。

特にApple Silicon環境では、プロセス生成の速度が従来の最大100倍にまで高速化されるという驚異的な改善が含まれています。

本記事では、C#開発者が知っておくべき.NET 11の新しいProcess APIの詳細と、それらがどのよう実務を効率化するのかを詳しく解説します。

.NET 11で追加された主要なProcess API一覧

.NET 11では、多くの高レベルAPIが導入され、わずか一行のコードでプロセスの実行と結果の取得が可能になりました。

まずは、今回新しく追加された機能を一覧表で確認してみましょう。

機能カテゴリー新しいAPI概要
ワンライナー実行Process.RunAndCaptureText[Async]プロセスを開始し、出力を取得して終了まで待機する一連の処理を一行で実行します。
出力取得なしの実行Process.Run[Async]プロセスを開始し、出力を取得せずに終了まで待機します。
リソース即時解放Process.StartAndForgetプロセスを開始してPIDを返し、関連するリソースを即座に解放します。
安全な出力読み取りProcess.ReadAllText/Bytes/Lines[Async]標準出力と標準エラー出力を同時に読み取り、パイプのバッファフルによるデッドロックを防ぎます。
ライフタイム管理ProcessStartInfo.KillOnParentExit親プロセスが終了した際に、子プロセスも確実に強制終了させる機能です。
ハンドル制御ProcessStartInfo.InheritedHandles子プロセスに継承させるハンドルを個別に指定し、不要なリークを防止します。
軽量APISafeProcessHandleの拡張トリミング(Trimming)に適した低レベルな制御APIを提供します。

直感的なプロセスの実行と出力取得

これまで、外部コマンドを実行してその出力を文字列として取得するには、多くのボイラープレートコードが必要でした。

.NET 11からは、Process.RunAndCaptureTextを使用することで、以下のように極めて簡潔に記述できます。

C#
// プロセスを実行し、標準出力とエラー出力を取得する
var result = Process.RunAndCaptureText("dotnet", ["--help"]);

// 終了コードの確認
Console.WriteLine($"Exit Code: {result.ExitStatus.ExitCode}");
// 標準出力の表示
Console.WriteLine($"Output: {result.StandardOutput}");

このAPIは内部的に、後述するデッドロック防止ロジックを自動的に適用しているため、安全に使用することが可能です。

デッドロック問題を根本から解決する新しい読み取り方式

従来の.NETで標準出力をリダイレクトする際、最も恐ろしい問題は「パイプのバッファ詰まりによるデッドロック」でした。

OSのパイプバッファ(Windowsで通常4KB、Unixで64KB)がフルになると、書き込み側のプロセスは読み取り側がデータを消費するまで停止してしまいます。

もし開発者がStandardOutput.ReadToEnd()を呼び出した後にStandardError.ReadToEnd()を呼び出していた場合、エラー出力がバッファ一杯に溜まるとプロセスが永久に停止するという罠がありました。

マルチプレクシングによる同時読み取りの導入

.NET 11で追加されたReadAllTextなどのメソッドは、この問題を解決するためにマルチプレクシング(多重化)を採用しています。

WindowsではWaitForMultipleObjects、Unix系ではpollというシステムコールを使用し、単一のスレッドで標準出力と標準エラー出力を同時に監視します。

これにより、どちらのストリームにデータが来ても即座に読み取ることができるため、バッファが溢れてプロセスが停止する心配がなくなりました。

C#
// 手動でプロセスを開始し、安全に出力を読み取る例
using var process = new Process { StartInfo = startInfo };
process.Start();

// stdoutとstderrをデッドロックなく同時に読み取る
var (output, error) = process.ReadAllText();
process.WaitForExit();

また、行単位でリアルタイムに処理したい場合のために、ReadAllLinesAsyncというIAsyncEnumerableを返すメソッドも用意されています。

C#
// 非同期ストリームで行ごとに処理する
await foreach (var line in process.ReadAllLinesAsync())
{
    if (line.StandardError)
    {
        Console.Error.WriteLine($"Error: {line.Content}");
    }
    else
    {
        Console.WriteLine(line.Content);
    }
}

OSレベルの最適化:Apple Siliconでの100倍高速化

今回のアップデートで最も注目すべきベンチマーク結果の一つが、Appleプラットフォームにおけるパフォーマンス向上です。

従来の.NETでは、Unix系環境でのプロセス生成にforkexecの組み合わせを使用していました。

.NET 11では、macOSおよびiOS系においてposix_spawnを使用するように内部実装が切り替えられました。

これにより、特にApple Silicon(M1/M2/M3/M4チップ)環境において、プロセスの開始速度が最大100倍に向上しています。

実行結果
// Apple M4でのベンチマーク例 (Startメソッドの実行時間)
.NET 10 (従来のfork方式): 12,043.2 us
.NET 11 (posix_spawn方式): 122.0 us
=> 約98.7倍の高速化

この改善は、マイクロサービスの実行やビルドツールなど、短時間に多数のプロセスを生成・破棄するアプリケーションにおいて劇的な効果を発揮します。

高度な制御を可能にするハンドル継承とリダイレクト

セキュリティやリソース管理の観点から、子プロセスが親プロセスのどのハンドルを継承するかを制御することは非常に重要です。

これまで.NETでは「すべてのハンドルを継承する」か「全くしないか」という極端な選択肢しかありませんでした。

新しく導入されたProcessStartInfo.InheritedHandlesプロパティを使用すると、継承させるハンドルを明示的にリストアップすることが可能です。

C#
// 特定のハンドルだけを継承させる設定
var startInfo = new ProcessStartInfo("worker.exe");
startInfo.InheritedHandles = new List<SafeHandle> { someFileHandle };

さらに、標準入出力のハンドルを直接指定できるStandardOutputHandleなどのプロパティも追加されました。

これにより、シェルを介さずにC#コードだけでプロセス間のパイプライン(ls | grepなど)を効率的に構築できるようになります。

C#
// プロセスAの出力をプロセスBの入力に直接繋ぐ例
SafeFileHandle.CreateAnonymousPipe(out var readPipe, out var writePipe);

var producer = new ProcessStartInfo("ls") { StandardOutputHandle = writePipe };
var consumer = new ProcessStartInfo("grep") { StandardInputHandle = readPipe };

Process.Start(producer);
Process.Start(consumer);

プロセスの孤立を防ぐ「KillOnParentExit」

親プロセスがクラッシュしたり強制終了された際、実行中の子プロセスが「ゾンビプロセス」として残ってしまう問題は、多くの開発者を悩ませてきました。

.NET 11では、OSレベルの機能を活用したKillOnParentExitプロパティが導入されました。

Windowsでは「ジョブオブジェクト(Job Object)」、Linuxでは「PR_SET_PDEATHSIG」を使用し、親プロセスがいなくなったことをOSが検知して自動的に子プロセスをクリーンアップします。

C#
var startInfo = new ProcessStartInfo("background-task.exe")
{
    // 親プロセスが死んだら自分も死ぬように設定
    KillOnParentExit = true
};
Process.Start(startInfo);

この機能により、アプリケーションの異常終了時でもシステムのリソースを食いつぶす心配がなくなり、より堅牢なシステム構築が可能になります。

NativeAOTとトリミングへの対応

.NET 11のProcess APIは、モダンなアプリケーション配布形態であるNativeAOTにも配慮されています。

従来のProcessクラスは非常に巨大で、トリミングによってバイナリサイズを小さくすることが困難でした。

新しく導入されたSafeProcessHandleベースの軽量APIを使用することで、NativeAOTバイナリのサイズを最大30%以上削減することが可能です。

クラウドネイティブな環境で、起動速度とイメージサイズが重視されるプログラムにおいて、この改善は大きなメリットとなります。

まとめ

.NET 11におけるProcess APIの刷新は、単なる機能追加に留まらない、歴史的なパラダイムシフトと言えます。

デッドロックのない安全な出力取得、一行で書ける便利なAPI、そしてAppleプラットフォームでの圧倒的な高速化など、実務におけるメリットは計り知れません。

特にRunAndCaptureTextKillOnParentExitは、これからのC#開発において標準的に使われる手法になるでしょう。

2026年5月のアップデートにより、C#は外部プロセスの操作においても他の言語を圧倒する生産性とパフォーマンスを手に入れました。

ぜひ、皆さんのプロジェクトでも.NET 11の新しいAPIを導入し、より安全で高速なアプリケーション開発を体験してください。