简体   繁体   English

异步进程启动并等待它完成

[英]Async process start and wait for it to finish

I am new to the thread model in .NET.我是 .NET 中的线程 model 的新手。 What would you use to:你会用什么来:

  1. Start a process that handles a file (process.StartInfo.FileName = fileName;) .启动处理文件的进程(process.StartInfo.FileName = fileName;)
  2. Wait for the user to close the process OR abandon the thread after some time.等待用户关闭进程或在一段时间后放弃线程。
  3. If the user closed the process, delete the file.如果用户关闭了进程,则删除该文件。

Starting the process and waiting should be done on a different thread than the main thread, because this operation should not affect the application.启动进程和等待应该在与主线程不同的线程上完成,因为这个操作不应该影响应用程序。

Example:例子:

My application produces an html report.我的应用程序生成 html 报告。 The user can right click somewhere and say "View Report" - now I retrieve the report contents in a temporary file and launch the process that handles html files ie the default browser.用户可以右键单击某处并说“查看报告”——现在我在一个临时文件中检索报告内容并启动处理 html 文件的进程,即默认浏览器。 The problem is that I cannot cleanup, ie delete the temp file.问题是我无法清理,即删除临时文件。

"and waiting must be async" - I'm not trying to be funny, but isn't that a contradiction in terms? “等待必须是异步的” - 我不是想搞笑,但这不是矛盾吗? However, since you are starting a Process , the Exited event may help:但是,由于您正在启动Process ,因此Exited事件可能会有所帮助:

ProcessStartInfo startInfo = null;
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += delegate {/* clean up*/};

If you want to actually wait (timeout etc), then:如果你想真正等待(超时等),那么:

if(process.WaitForExit(timeout)) {
    // user exited
} else {
    // timeout (perhaps process.Kill();)
} 

For waiting async, perhaps just use a different thread?对于等待异步,也许只是使用不同的线程?

ThreadPool.QueueUserWorkItem(delegate {
    Process process = Process.Start(startInfo);
    if(process.WaitForExit(timeout)) {
        // user exited
    } else {
        // timeout
    }
});

Adding an advanced alternative to this old question.为这个老问题添加一个高级替代方案。 If you want to wait for a process to exit without blocking any thread and still support timeouts, try the following:如果您想等待进程退出而不阻塞任何线程并且仍然支持超时,请尝试以下操作:

    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;
    }

Again, the advantage to this approach compared to the accepted answer is that you're not blocking any threads, which reduces the overhead of your app.同样,与公认的答案相比,这种方法的优势在于您不会阻塞任何线程,从而减少了应用程序的开销。

Try the following code.试试下面的代码。

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
}

The .NET 5 introduced the new API Process.WaitForExitAsync , that allows to wait asynchronously for the completion of a process. .NET 5 引入了新的 API Process.WaitForExitAsync ,允许异步等待进程完成。 It offers the same functionality with the existing Process.WaitForExit , with the only difference being that the waiting is asynchronous, so it does not block the calling thread.它提供与现有Process.WaitForExit相同的功能,唯一的区别是等待是异步的,因此它不会阻塞调用线程。

Usage example:使用示例:

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!");
}

In the above example the UI remains responsive while the user interacts with the opened file.在上面的示例中,当用户与打开的文件交互时,UI 保持响应。 The UI thread would be blocked if the WaitForExit had been used instead.如果改为使用WaitForExit ,则 UI 线程将被阻塞。

I would probably not use a separate process for opening a file.我可能不会使用单独的进程来打开文件。 Instead, I'd probably utilize a background thread (if I thought the operation was going to take a long time and possible block the UI thread).相反,我可能会使用后台线程(如果我认为该操作需要很长时间并且可能会阻塞 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
}

Of course, this is not a good working example (it returns nothing) and I haven't shown how the UI gets updated (you have to use BeginInvoke at the UI level because a background thread cannot update the UI thread).当然,这不是一个好的工作示例(它不返回任何内容),并且我没有展示 UI 是如何更新的(您必须在 UI 级别使用 BeginInvoke,因为后台线程无法更新 UI 线程)。 But this approach is generally how I go about handling asynchronous operations in.Net.但是这种方法一般是我在.Net中如何处理异步操作的。

You can use the Exited event in Process class您可以在进程 class 中使用Exited事件

ProcessStartInfo info = new ProcessStartInfo();

info.FileName = "notepad.exe";
Process process = Process.Start(info);

process.Exited += new EventHandler(process_Exited);
Console.Read();

and in that event you can handle the operations you mentioned在那种情况下,您可以处理您提到的操作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM