I have a follow-up question to this post. In my version, I have the following I want to make asynchronous. Here is what I have:
public virtual Task<bool> ExecuteAsync()
{
var tcs = new TaskCompletionSource<bool>();
string exe = Spec.GetExecutablePath();
string args = string.Format("--input1={0} --input2={1}", Input1, Input2);
try
{
var process = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
process.Exited += (sender, arguments) =>
{
if (process.ExitCode != 0)
{
string errorMessage = process.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process.StandardOutput.ReadToEnd());
tcs.SetResult(true);
}
process.Dispose();
};
process.Start();
}
catch (Exception e)
{
Logger.InfoOutputWindow(e.Message);
tcs.SetResult(false);
return tcs.Task;
}
return tcs.Task;
}
}
Here Spec, Input1, Input2, CaseDir, LogFile
are all members of the class of which the ExecuteAsync is a method. Is that OK to use them like that? The parts I am struggling with are:
- I can't seem to use the async keyword at the method definition (
public virtual async Task<bool> ExecuteAsync()
) without a warning that I need anawait
keyword, whereas I do have one in the lambda expression for the process. Do I even need the async keyword at the method definition? I've seen supposedly asynchronous examples where they don't use it, e.g. this one. If I take it out it compiles, but can I use this asynchronously? - Is my usage of the async keyword in the lambda expression and the corresponding
await process.StandardError.ReadToEndAsync()
OK inside the process lambda expression? In this example, they don't useasync await
at the corresponding line, so I wonder how they got away with it? Wouldn't leaving it out make it blocking, since I was told that the methodReadToEnd
is blocking? - Is my call to
File.WriteAllText(LogFile, process.StandardOutput.ReadToEnd())
going to render the whole method blocking? If so, how can I avoid that, if at all possible? - Does the exception handling make sense? Should I be aware of any details of the application logger method
Logger.InfoOutputWindow
which I've used in thecatch
block? - Finally, why does the
process.Exited
event always appear before theprocess.Start()
in all the examples I've come across? Can I put theprocess.Start()
before theprocess.Exited
event?
Appreciate any ideas and thanks in advance for your interest & attention.
EDIT #1:
For #3 above, I had an idea, based in part on comment from @René Vogt below, so I made a change to move the File.WriteAllText(...)
call inside the else {}
block of the process.Exited
event. Perhaps this addresses #3.
EDIT #2:
I made the initial list of changes (code snippet is changed now), basically removed both the async
keyword at function definition and the await
keyword in the process.Exited
event handler based on original comments from @René Vogt. Haven't yet tried his most recent changes below. When I run, I get an exception:
A plugin has triggered error: System.InvalidOperationException; An attempt was made to transition a task to a final state when it had already completed.
The application log has call stack as follows:
UNHANDLED EXCEPTION:
Exception Type: CLR Exception (v4)
Exception Details: No message (.net exception object not captured)
Exception Handler: Unhandled exception filter
Exception Thread: Unnamed thread (id 29560)
Report Number: 0
Report ID: {d80f5824-ab11-4626-930a-7bb57ab22a87}
Native stack:
KERNELBASE.dll+0x1A06D RaiseException+0x3D
clr.dll+0x155294
clr.dll+0x15508E
<unknown/managed> (0x000007FE99B92E24)
<unknown/managed> (0x000000001AC86B00)
Managed stack:
at System.Threading.Tasks.TaskCompletionSource`1.SetException(Exception exception)
at <namespace>.<MyClass>.<>c__DisplayClass3.<ExecuteAsync>b__2(Object sender, EventArgs arguments)
at System.Diagnostics.Process.RaiseOnExited()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Object state, Boolean timedOut)