[英]Async process start and wait for it to finish
我是 .NET 中的線程 model 的新手。 你會用什么來:
(process.StartInfo.FileName = fileName;)
。啟動進程和等待應該在與主線程不同的線程上完成,因為這個操作不應該影響應用程序。
例子:
我的應用程序生成 html 報告。 用戶可以右鍵單擊某處並說“查看報告”——現在我在一個臨時文件中檢索報告內容並啟動處理 html 文件的進程,即默認瀏覽器。 問題是我無法清理,即刪除臨時文件。
“等待必須是異步的” - 我不是想搞笑,但這不是矛盾嗎? 但是,由於您正在啟動Process
,因此Exited
事件可能會有所幫助:
ProcessStartInfo startInfo = null;
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += delegate {/* clean up*/};
如果你想真正等待(超時等),那么:
if(process.WaitForExit(timeout)) {
// user exited
} else {
// timeout (perhaps process.Kill();)
}
對於等待異步,也許只是使用不同的線程?
ThreadPool.QueueUserWorkItem(delegate {
Process process = Process.Start(startInfo);
if(process.WaitForExit(timeout)) {
// user exited
} else {
// timeout
}
});
為這個老問題添加一個高級替代方案。 如果您想等待進程退出而不阻塞任何線程並且仍然支持超時,請嘗試以下操作:
public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
{
ManualResetEvent processWaitObject = new ManualResetEvent(false);
processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
RegisteredWaitHandle registeredProcessWaitHandle = null;
registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
processWaitObject,
delegate(object state, bool timedOut)
{
if (!timedOut)
{
registeredProcessWaitHandle.Unregister(null);
}
processWaitObject.Dispose();
tcs.SetResult(!timedOut);
},
null /* state */,
timeout,
true /* executeOnlyOnce */);
return tcs.Task;
}
同樣,與公認的答案相比,這種方法的優勢在於您不會阻塞任何線程,從而減少了應用程序的開銷。
試試下面的代碼。
public void KickOffProcess(string filePath) {
var proc = Process.Start(filePath);
ThreadPool.QueueUserWorkItem(new WaitCallBack(WaitForProc), proc);
}
private void WaitForProc(object obj) {
var proc = (Process)obj;
proc.WaitForExit();
// Do the file deletion here
}
.NET 5 引入了新的 API Process.WaitForExitAsync
,允許異步等待進程完成。 它提供與現有Process.WaitForExit
相同的功能,唯一的區別是等待是異步的,因此它不會阻塞調用線程。
使用示例:
private async void button1_Click(object sender, EventArgs e)
{
string filePath = Path.Combine
(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Guid.NewGuid().ToString() + ".txt"
);
File.WriteAllText(filePath, "Hello World!");
try
{
using Process process = new();
process.StartInfo.FileName = "Notepad.exe";
process.StartInfo.Arguments = filePath;
process.Start();
await process.WaitForExitAsync();
}
finally
{
File.Delete(filePath);
}
MessageBox.Show("Done!");
}
在上面的示例中,當用戶與打開的文件交互時,UI 保持響應。 如果改為使用WaitForExit
,則 UI 線程將被阻塞。
我可能不會使用單獨的進程來打開文件。 相反,我可能會使用后台線程(如果我認為該操作需要很長時間並且可能會阻塞 UI 線程)。
private delegate void FileOpenDelegate(string filename);
public void OpenFile(string filename)
{
FileOpenDelegate fileOpenDelegate = OpenFileAsync;
AsyncCallback callback = AsyncCompleteMethod;
fileOpenDelegate.BeginInvoke(filename, callback, state);
}
private void OpenFileAsync(string filename)
{
// file opening code here, and then do whatever with the file
}
當然,這不是一個好的工作示例(它不返回任何內容),並且我沒有展示 UI 是如何更新的(您必須在 UI 級別使用 BeginInvoke,因為后台線程無法更新 UI 線程)。 但是這種方法一般是我在.Net中如何處理異步操作的。
您可以在進程 class 中使用Exited
事件
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "notepad.exe";
Process process = Process.Start(info);
process.Exited += new EventHandler(process_Exited);
Console.Read();
在那種情況下,您可以處理您提到的操作
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.