方法中的多个进程从调用线程异步执行

时间:2016-06-16 21:42:41

标签: c# asynchronous process task

假设我有多个(比方说,两个)进程,我想按顺序但异步运行,我该怎么做呢?请参阅以下代码段:

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

}

提前感谢您的想法。

编辑#1:

当我运行上面显示的代码时,在成功完成第一个过程后失败,并显示以下错误:

"System.InvalidOperationException; An attempt was made to transition a task to a final state when it had already completed".

当我仅使用第一个进程运行代码(删除所有与另一个进程相关的代码)时,它运行正常。

1 个答案:

答案 0 :(得分:2)

您看到的错误与这两个进程无关。它是由多次设置TaskCompletionSource的SetResult方法引起的。你不能给同一个任务两个不同的结果。

有一些重要的变化:

  1. 不要因为第一个过程成功而将任务完成设置为true。您希望它等到第二个过程完成后才能确定它是否真的成功。删除对&#39; tsc.SetResult(true)&#39;的呼叫。 in process1.Exited。
  2. 你应该只在process1完成后启动process2(你说你希望它们是同步的。为此,在process1的退出处理程序中启动process2。另外,我假设你不想开始process2如果process1失败
  3. 将process2.Exited处理程序移到上面调用process1.Start()的位置。这只是在process1和process2在设置了process2的Exited处理程序之前非常快速地完成时避免了竞争条件。
  4. 这是未经测试的,但您的代码最终应该看起来像这样:

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