Async Process Start and Wait for It to Finish

Is there any async equivalent of Process.Start?

Process.Start() only starts the process, it doesn't wait until it finishes, so it doesn't make much sense to make it async. If you still want to do it, you can do something like await Task.Run(() => Process.Start(fileName)).

But, if you want to asynchronously wait for the process to finish, you can use the Exited event together with TaskCompletionSource:

static Task<int> RunProcessAsync(string fileName)
{
var tcs = new TaskCompletionSource<int>();

var process = new Process
{
StartInfo = { FileName = fileName },
EnableRaisingEvents = true
};

process.Exited += (sender, args) =>
{
tcs.SetResult(process.ExitCode);
process.Dispose();
};

process.Start();

return tcs.Task;
}

Async process start and wait for it to finish

The .NET 5 introduced the new API Process.WaitForExitAsync, that allows to wait asynchronously for the completion of a process. 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.

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. The UI thread would be blocked if the WaitForExit had been used instead.

How to wait for async method to complete?

Avoid async void. Have your methods return Task instead of void. Then you can await them.

Like this:

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
foreach (byte[] b in byteArrays)
{
while (condition)
{
// we'll typically execute this code many times until the condition is no longer met
Task t = SendOutputReportViaInterruptTransfer();
await t;
}

// read some data from device; we need to wait for this to return
await RequestToGetInputReport();
}
}

private async Task RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}

Process.WaitForExit() asynchronously

process.EnableRaisingEvents = true;

process.Exited += [EventHandler]

Wait until a process ends

I think you just want this:

var process = Process.Start(...);
process.WaitForExit();

See the MSDN page for the method. It also has an overload where you can specify the timeout, so you're not potentially waiting forever.

.py file executed by C# process not waiting to finish

I recently created something similar and ended up with this because, whilst waiting for the process is easy, it is tricky to get the output stream filled correctly.

The method presented also allow you to display the output into a textblock or similar in your application.

If you use it like this, the token will be written to the StringBuilder, and used as return value.

private async Task<string> RunCommand(string fileName, string args)
{
var timeoutSignal = new CancellationTokenSource(TimeSpan.FromMinutes(3));
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = fileName;
start.Arguments = string.Format("{0}", args);
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.UseShellExecute = false;
start.CreateNoWindow = true;

var sb = new StringBuilder();
using (Process process = new Process())
{
process.StartInfo = start;
process.OutputDataReceived += (sender, eventArgs) =>
{
sb.AppendLine(eventArgs.Data); //allow other stuff as well
};
process.ErrorDataReceived += (sender, eventArgs) => {};

if (process.Start())
{
process.EnableRaisingEvents = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();

await process.WaitForExitAsync(timeoutSignal.Token);
//allow std out to be flushed
await Task.Delay(100);
}
}
return sb.ToString();
}

To render this to a textblock in a UI application, you'll need to:

  • implement an event which signals a new line has been read, which means forwarding the process.OutputDataReceived event.
  • if your thinking about a live feed, make sure you flush the stdio buffer in python setting flush to true: print(""hello world"", flush=True)

If you're using an older .net version; you can implement the WaitForExitAsync as described here: https://stackoverflow.com/a/17936541/2416958 as an extention method:

public static class ProcessHelpers
{
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;
}
}

Start multiple Processes and wait till they are finished

Lets have a look why it works like it does at the moment:

Thread.Join Doc

Blocks the calling thread until the thread represented by this instance terminates ...

It 'waits' for the threads (t1, t2) to terminate before continuing with sendMail().

Task.WhenAll Doc

Creates a task that will complete when all of the supplied tasks have completed.

This one does not wait. It is creating another Task an continues on as usual.

Task.WhenAll.Wait Doc

Waits for the Task to complete execution.

This one takes the Task create above and waits for its termination. (Task.WaitAll would have achived the same)

What you can do:

If you want to block the current thread until completion:

Task.WaitAll(tl.ToArray());

If you dont want to block the current thread:

Task.WhenAll(tl).ContinueWith(t => sendMail());

ContinueWith Creates a continuation that executes asynchronously
when the target Task completes.



Related Topics



Leave a reply



Submit