Reputation: 1356
Suppose I have multiple (say, two) processes that I want to run sequentially but asynchronously, how do I go about doing this? See snippet below:
public virtual Task<bool> ExecuteAsync()
{
var tcs = new TaskCompletionSource<bool>();
string exe1 = Spec.GetExecutablePath1();
string exe2 = Spec.GetExecutablePath2();
string args1 = string.Format("--input1={0} --input2={1}", Input1, Input2);
string args2 = string.Format("--input1={0} --input2={1}", Input1, Input2);
try
{
var process1 = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe1,
Arguments = args1,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
var process2 = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe2,
Arguments = args2,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
process1.Exited += (sender, arguments) =>
{
if (process1.ExitCode != 0)
{
string errorMessage = process1.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process1 did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process1.StandardOutput.ReadToEnd());
tcs.SetResult(true);
}
process1.Dispose();
};
process1.Start();
process2.Exited += (sender, arguments) =>
{
if (process2.ExitCode != 0)
{
string errorMessage = process2.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process2 did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process2.StandardOutput.ReadToEnd());
tcs.SetResult(true);
}
process2.Dispose();
};
process2.Start();
}
catch (Exception e)
{
Logger.InfoOutputWindow(e.Message);
tcs.SetResult(false);
return tcs.Task;
}
return tcs.Task;
}
}
Thanks in advance for your ideas.
EDIT #1:
When I run the code as shown above it fails with the error below after successfully completing the first process:
"System.InvalidOperationException; An attempt was made to transition a task to a final state when it had already completed".
When I run the code with just the first process (delete all code pertaining to the other), it runs OK.
Upvotes: 0
Views: 684
Reputation: 3302
The error you're seeing is unrelated to the two processes. It's caused by setting the SetResult method of the TaskCompletionSource multiple times. You can't give the same task two different results.
There are a few important changes:
This is untested, but your code should end up looking something like this:
public virtual Task<bool> ExecuteAsync()
{
var tcs = new TaskCompletionSource<bool>();
string exe1 = Spec.GetExecutablePath1();
string exe2 = Spec.GetExecutablePath2();
string args1 = string.Format("--input1={0} --input2={1}", Input1, Input2);
string args2 = string.Format("--input1={0} --input2={1}", Input1, Input2);
try
{
var process1 = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe1,
Arguments = args1,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
var process2 = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
UseShellExecute = false,
FileName = exe2,
Arguments = args2,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDir = CaseDir
}
};
process1.Exited += (sender, arguments) =>
{
if (process1.ExitCode != 0)
{
string errorMessage = process1.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process1 did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process1.StandardOutput.ReadToEnd());
process2.Start();
}
process1.Dispose();
};
process2.Exited += (sender, arguments) =>
{
if (process2.ExitCode != 0)
{
string errorMessage = process2.StandardError.ReadToEndAsync();
tcs.SetResult(false);
tcs.SetException(new InvalidOperationException("The process2 did not exit correctly. Error message: " + errorMessage));
}
else
{
File.WriteAllText(LogFile, process2.StandardOutput.ReadToEnd());
tcs.SetResult(true);
}
process2.Dispose();
};
process1.Start();
}
catch (Exception e)
{
Logger.InfoOutputWindow(e.Message);
tcs.SetResult(false);
return tcs.Task;
}
return tcs.Task;
}
Upvotes: 2