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

C# 方法中的多个进程从调用线程异步执行,c#,asynchronous,process,task,C#,Asynchronous,Process,Task,假设我有多个(比如说,两个)进程,我想顺序但异步地运行,我该怎么做呢?请参阅下面的代码片段: public virtual Task<bool> ExecuteAsync() { var tcs = new TaskCompletionSource<bool>(); string exe1 = Spec.GetExecutablePath1(); string exe2 = Spec.GetExecutablePath2(); string

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

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

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

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

有几个重要的变化:

  • 不要仅仅因为第一个进程成功就将任务完成设置为true。您希望它等到第二个过程完成后再决定它是否真的成功。删除对process1.Exited中的“tsc.SetResult(true)”的调用
  • 您应该只在process1完成后启动process2(您说过希望它们是同步的。要做到这一点,请在process1的退出处理程序中启动process2。此外,如果process1失败,我假设您不希望启动process2
  • 将process2.Exited处理程序向上移动到调用process1.Start()的位置。这只是避免了当process1和process2在设置process2的Exited处理程序之前很快完成时出现争用情况
  • 这是未经测试的,但您的代码最终应该是这样的:

    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;
    }
    
    公共虚拟任务ExecuteAsync()
    {
    var tcs=new TaskCompletionSource();
    字符串exe1=Spec.GetExecutablePath1();
    字符串exe2=Spec.GetExecutablePath2();
    字符串args1=string.Format(“--input1={0}--input2={1}”,input1,input2);
    字符串args2=string.Format(“--input1={0}--input2={1}”,input1,input2);
    尝试
    {
    var process1=新流程
    {
    EnableRaisingEvents=true,
    StartInfo=
    {
    UseShellExecute=false,
    FileName=exe1,
    参数=args1,
    重定向标准输出=真,
    RedirectStandardError=true,
    WorkingDir=CaseDir
    }
    };
    var process2=新流程
    {
    EnableRaisingEvents=true,
    StartInfo=
    {
    UseShellExecute=false,
    FileName=exe2,
    参数=args2,
    重定向标准输出=真,
    RedirectStandardError=true,
    WorkingDir=CaseDir
    }
    };
    process1.退出+=(发送方,参数)=>
    {
    if(process1.ExitCode!=0)
    {
    string errorMessage=process1.StandardError.ReadToEndAsync();
    tcs.SetResult(假);
    tcs.SetException(新的InvalidOperationException(“进程1未正确退出。错误消息:“+errorMessage”);
    }
    其他的
    {
    WriteAllText(日志文件,process1.StandardOutput.ReadToEnd());
    process2.Start();
    }
    process1.Dispose();
    };
    process2.退出+=(发送方,参数)=>
    {
    if(process2.ExitCode!=0)
    {
    string errorMessage=process2.StandardError.ReadToEndAsync();
    tcs.SetResult(假);
    tcs.SetException(新的InvalidOperationException(“进程2未正确退出。错误消息:“+errorMessage”);
    }
    其他的
    {
    WriteAllText(日志文件,process2.StandardOutput.ReadToEnd());
    tcs.SetResult(真);
    }
    process2.Dispose();
    };
    process1.Start();
    }
    捕获(例外e)
    {
    Logger.InfoOutputWindow(e.Message);
    tcs.SetResult(假);
    返回tcs.Task;
    }
    返回tcs.Task;
    }
    
    您看到的错误与这两个进程无关。它是由多次设置TaskCompletionSource的SetResult方法引起的。您不能为同一个任务提供两个不同的结果

    有几个重要的变化:

  • 不要仅仅因为第一个进程成功就将任务完成设置为true。您希望它等到第二个进程完成后再决定它是否真的成功。请删除对process1中“tsc.SetResult(true)”的调用。已退出
  • 您应该只在process1完成后启动process2(您说过希望它们是同步的。要做到这一点,请在process1的退出处理程序中启动process2。此外,如果process1失败,我假设您不希望启动process2
  • 将process2.Exited处理程序向上移动到调用process1.Start()的位置。这只是避免了当process1和process2在设置process2的Exited处理程序之前很快完成时出现争用情况
  • 这是未经测试的,但您的代码最终应该是这样的:

    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;
    }
    
    公共虚拟任务ExecuteAsync()
    {
    var tcs=new TaskCompletionSource();
    字符串exe1=Spec.GetExecutablePath1();
    字符串exe2=Spec.GetExecutablePath2();
    字符串args1=string.Format(“--input1={0}--input2={1}”,input1,input2);
    字符串args2=string.Format(“--input1={0}--input2={1}”,input1,input2);
    尝试
    {
    var process1=新流程
    {
    EnableRaisingEvents=true,
    StartInfo=
    {
    UseShellExecute=false,
    FileName=exe1,
    参数=args1,
    重定向标准输出=真,
    RedirectStandardError=true,
    WorkingDir=CaseDir
    }
    };
    var process2=新流程
    {
    EnableRaisingEvents=true,
    StartInfo=
    {
    UseShellExecute=false,
    FileName=exe2,
    参数=args2,
    重定向标准输出=真,
    RedirectStandardError=true,
    WorkingDir=CaseDir
    }
    };