在 .NET 開發中,System.Diagnostics.Process 是我們與作業系統互動、啟動外部程式的主要工具。然而,對於許多工程師(尤其是剛接手系統層級開發的 Junior)來說,使用這個類別時常會遇到一些隱晦的陷阱,例如程式莫名其妙地卡死(Deadlock)或產生無法關閉的孤兒行程。
.NET 11 對 Process API 進行了多年來最大規模的更新,不僅簡化了開發流程,更從底層解決了效能與資源洩漏的問題。
解決 Process 輸出讀取中的死結問題
許多工程師在重導向(Redirect)子行程的標準輸出(stdout)與標準錯誤(stderr)時,會習慣先呼叫 WaitForExit(),然後再讀取輸出內容。這在輸出量較少時沒問題,但一旦輸出內容超過了作業系統的 Pipe Buffer(管道緩衝區,Windows 約 4KB,Unix 約 64KB),就會發生死結。
死結的原因在於:子行程因為緩衝區滿了而阻塞在寫入操作,等待父行程讀取資料;而父行程則阻塞在 WaitForExit(),等待子行程結束。兩者互等,程式永久卡死。
為了徹底解決這個問題,.NET 11 引入了多路復用(Multiplexing)技術,讓父行程能同時監控並讀取兩個流,而不再需要開發者自行處理複雜的非同步任務或事件。
新推出的便捷 API
為了讓開發者不再需要寫冗長的非同步讀取代碼,.NET 11 提供了多個一鍵式方法:
RunAndCaptureText 與 RunAsync 這組 API 是最推薦的選擇。它將啟動行程、捕捉 stdout 與 stderr、等待結束這三個步驟封裝在一起。你只需要一行代碼就能拿到所有輸出結果與結束狀態(ProcessExitStatus),且完全沒有死結風險。
ReadAllText 與 ReadAllLines 如果你已經啟動了 Process,可以使用這組方法一次性讀取所有輸出。ReadAllLines 則是以列為單位回傳,方便你根據輸出內容(例如判斷是否為錯誤列)來改變顯示顏色或進行處理。
StartAndForget 在某些場景下,我們只想啟動一個程式(例如開啟記事本)而不需要追蹤它的生命週期。以往即使呼叫 Dispose 也不會殺掉子行程,現在可以使用 StartAndForget 直接啟動並立即釋放所有相關資源,僅回傳 PID(行程識別碼)。
更精細的句柄(Handle)與生命週期管理
句柄(Handle)是作業系統用來識別資源的索引。在啟動子行程時,預設會繼承父行程的所有可繼承句柄,這可能導致子行程意外持有不該擁有的資源,甚至導致 Pipe 永遠無法關閉。
精準控制繼承 透過 ProcessStartInfo.InheritedHandles 屬性,開發者現在可以明確指定哪些句柄需要被子行程繼承,而非全部繼承。這不僅提升了安全性,在 Windows 上還能顯著提升並行啟動行程的吞吐量。
靈活的重導向 現在你可以將標準輸入、輸出、錯誤直接導向到任何 SafeFileHandle。這讓 C# 能輕鬆實現類似 Shell 的管道操作(例如:ls grep),或者將輸出直接導向到檔案或空句柄(Null Handle)以捨棄輸出。
防止孤兒行程 ProcessStartInfo.KillOnParentExit 解決了子行程在父行程崩潰後依然在背景運行的問題。它在 Windows 上利用 Job Object,在 Linux 上利用 PR_SET_PDEATHSIG 實現,確保子行程會隨父行程一起終止。
效能與 NativeAOT 優化
除了功能增加,.NET 11 在底層做了大量工程優化:
Windows 擴展性提升 過去 BeginOutputReadLine 會阻塞執行緒池(ThreadPool)執行緒。現在 Windows 上的匿名管道改用命名管道(Named Pipe)實現,支援真正的非同步 I/O,大幅提升了大量並行啟動行程時的系統效能。
Apple 平台加速 在 macOS 與 Apple Silicon 上,啟動行程的方式從 fork + exec 遷移至 posix_spawn。這帶來了驚人的效能提升,某些場景下啟動速度快了近 100 倍。
更小的二進位檔案 對於使用 NativeAOT 的專案,引入了輕量級的 SafeProcessHandle API。如果你不需要 Process 類別提供的完整功能,僅使用 SafeProcessHandle 可以減少高達 32% 的二進位檔案體積。
總結
.NET 11 的 Process API 更新將開發重心從處理底層同步問題(如死結、句柄繼承)移回了業務邏輯。對於大多數開發者,建議優先使用 RunAndCaptureText;對於需要極高控制權或追求極小體積的 AOT 應用,則可選擇 SafeProcessHandle。
來源:devblogs.microsoft.com - Process API Improvements in .NET 11
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。