启动多个进程并异步重定向输出

Start multiple process and redirect output async

提问人:Pietro di Caprio 提问时间:7/18/2018 最后编辑:Panagiotis KanavosPietro di Caprio 更新时间:7/25/2018 访问量:1108

问:

我正在用一些对我来说很困难的事情来挑战自己。

我想要一个运行“从属终端”的“主终端”。 从站使用“重定向输出”选项 (RedirectStandardOutput) 启动。 从站必须异步运行,主站应从站管理“ReadLine”。 当从站运行时,主站必须能够执行其他操作。

我的实际代码:

static void StartTerminal(string ExecPath, int DefaultPort, string Arguments = "")
    {
        int port = DefaultPort;
        if (usedPorts.Contains(port)) port = nextPort;
        while(usedPorts.Contains(nextPort))
        {
            nextPort++;
            port = nextPort;
        }
        usedPorts.Add(port);
        string _arguments = "/port:" + port;
        _arguments += Arguments;

        //* Create your Process
        Process process = new Process();
        process.StartInfo.FileName = "dotnet ";
        process.StartInfo.Arguments = ExecPath + " /c " + _arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        //* Set your output and error (asynchronous) handlers
        process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
        process.ErrorDataReceived += new DataReceivedEventHandler(OutErrorHandler);
        //* Start process and handlers

        ThreadStart ths = new ThreadStart(() => {
            bool ret = process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
        });
        Thread th = new Thread(ths);
        th.Start();

        //process.Start();
        //process.BeginOutputReadLine();
        //process.BeginErrorReadLine();
        //process.WaitForExit();
    }

如您所见,我尝试了启动和不启动新线程。 线程似乎很好,我在从属终端完成操作之前到达了主空的末尾,但随后我在从属终端的“ReadLine”请求中被阻止。用户必须按Enter键才能继续,我不想要这个。

你能帮帮我吗? 你建议采取不同的方法吗?

谢谢,问候。

编辑1:我想管理从属终端等待“Enter”的情况,因为它可能由第三方开发,这些第三方可能会忽略我说不使用的指南(好吧,这不是我的问题,但我喜欢愚蠢的证明软件)。 主终端管理着数量不明的从属终端,并记录从站的所有输出,并运行一个看门狗,如果从站崩溃,该看门狗会重新启动它。 主站还接收启动/停止/重启从站的命令和/或其他命令。Console.ReadLine()

C# 多线程异步 进程

评论

0赞 Zero 7/18/2018
我想我终于明白了,所以本质上运行子进程,它可能包含一个(或其他命令,可以阻止程序流)。我认为你需要的是异步踏板或任务,每个踏板或任务都应该调用你的方法。我会尽快更新我的答案StartTerminalReadLineStartTerminal( ... )
0赞 Panagiotis Kanavos 7/18/2018
您不需要线程来启动进程。根据定义,单独的进程在不同的线程上运行。只需致电process.Start()
0赞 Panagiotis Kanavos 7/18/2018
你到底想做什么?此代码中没有任何与多线程相关的内容,只有两个进程通过其输入/输出流进行通信。如果您想并行或异步处理数据,有许多方法比使用两个线程更快、更好、更具可扩展性 100000 倍。例如,将并行使用所有内核处理数据。您要解决的实际问题是什么?Parallel.ForEach()
0赞 Panagiotis Kanavos 7/18/2018
如果希望管道中的两个或多个辅助角色处理数据,请检查 Dataflow 类,例如 ActionBlock、TransformBlock。

答:

2赞 Zero 7/18/2018 #1

根据 Process Class 文档,两者都 和 execute,这意味着它们不会阻止程序执行(或者换句话说:程序不会等待这些方法完成执行)。Process.BeginOutputReadLine();Process.BeginErrorReadLine();asynchronously

您可能希望设置一些布尔值来指示数据何时完成读入和事件处理程序,并在 main 方法中添加一个循环,该循环在执行两个方法之前不允许程序完成。OutputDataReceivedErrorDataReceived

private void DataReceivedEventHandler( [parameters] ){
    //I believe this is the part where you read the actual stream
    outputDataReceived = true;
}//do the same for ErrorDataReceived
while (!outputDataReceived && !errorDataReceived){
   wait(1000); //The actual method might be different, maybe sleep( 1000 ) or thread.sleep ( you can also set a different interval )
}

编辑(删除了不相关的编辑):步任务、任务和并行执行在此问题的答案中进行了描述。我相信任何一个答案都会满足您的需求。

基本上,在链接问题的答案中使用任何代码示例,只需在线程/任务/并行调用中调用您的方法即可。使用答案中的代码创建异步处理并实现逻辑,以防止程序到达其末尾,直到所有线程完成其工作。伪代码:StartTerminal( )

List<Thread> threads = new List<Thread>();

private void StartTerminal(int id, params){
     //all of your code keep it asynchornous

    while( !outputDataReceived && !errorDataReceived ){
        sleep( 1000 ); //delay between checks
    } //Makes sure this thread does not close until data is received, implement any other logic that should keep the thread alive here
}

public static void main(...){

    foreach(ParameterSet params in Parameters){ //Create thread list with different parameters
       var thread =  new Thread( StartTerminal(params) );
       threads.Add( thread );
    }

    while( !threads.isEmpty() ) ){ //if it is empty, all threads finished the job and got deleted, loop is skipped and program closes
        var threadsToRemove = new List<Thread>();
        foreach(Thread t in threads){ //start unstarted threads
          if(t.ThreadState == ThreadState.Unstarted){
            threads[i].Start( ).
          }elseif(t.ThreadState == ThreadState.Stopped){ //remove stopped threads
            threadsToRemove.Add(t).
          }//Implement other logic for suspended, aborted and other threads...
        }
        foreach(Thread t in threadsToRemove){
           threads.Remove(t);
        }

        sleep(1000); //delay between checks
    }
}

编辑2:将这些模式交给你的从属终端开发人员(如果他们不知道如何自己做,而不是阻止所有内容),因为一旦同步调用“ReadLine”,在用户按回车键之前,你什么也做不了(合理)。

如果可能(尤其是在对硬件进行编程时,例如控制器),请使用触发器而不是“ReadLines”进行用户输入

HardwareButtonEnter.Press += delegate{ userInput = eventParameters.userInput; 
    UserInputReceived = true;}; //This is really hardware API specific code, only the boolean is important

//start an async thread/process for reading master terminal input.
while(1 = 1){
   if(UserInputReceived){
      ProcessUserInput().
   }elseif(masterTerminalDataReceived){
      ProcessMasterTerminalData().
   }
}

如果无法使用触发器(例如,在对控制台应用程序进行编程时),请将阻塞语句放在异步线程中,并在线程完成时处理它们。

//Start your (async) ReadLine thread. Make sure it also sets UserInputReceived = true when it ends
//Start your async process read thread. also get it to set some boolean.
while( 1 = 1){
   if(threads.Any( t => t.Status = Finished ){
      if(UserInputReceived){
      }elseif(terminalInputReceived){
       ..
      }
   }
}

评论

0赞 Pietro di Caprio 7/18/2018
之后的代码不会执行,直到用户在从属终端请求时按 Enter 键。我需要避免在需要时管理“自动输入”。StartTerminal([params]);
0赞 Zero 7/18/2018
您能否更新您的问题,解释您想要实现的目标(一步一步),因为 ReadLine 方法仅在按下 Enter 时执行(提交一行)。如果您不需要用户的任何输入,也许可以完全删除 ReadLine?如果您确实需要用户的输入,我不明白为什么您需要等待用户。您是否只等待“Enter”,因为从属终端“不知道”服务器何时完成作业?
0赞 Pietro di Caprio 7/18/2018
问题已编辑。我想忽略不应存在于从属终端中的 ReadLine()。
0赞 Pietro di Caprio 7/21/2018
Fianlaly 测试了代码!我不得不稍微改变一下,但没关系;)我仍然不知道如何管理从属终端。它仍然阻止了我的主终端..在启动进程时使用,线程(当 ReadLine 调用时)进入,但如果我这样做,就会出现异常。有什么想法吗?Console.ReadLine()process.WaitForExit();System.Threading.ThreadState.WaitSleepJointhread.Interrupt()
0赞 Zero 7/24/2018
这里的关键是异步启动所有线程/任务。如果你正确地启动了你的线程(这是你应该用一个简单的用例来研究和测试的部分),单个线程将被阻塞(通过阻塞命令),但主进程应该继续进行。我不记得具体是怎么回事,但我认为这与我在回答中写的相似。