Process sometimes hangs while waiting for Exit

前端 未结 4 1257
天命终不由人
天命终不由人 2021-01-11 10:23

What may be the reason of my process hanging while waiting for exit?

This code has to start powershell script which inside performs many action e.g start recompiling

4条回答
  •  北荒
    北荒 (楼主)
    2021-01-11 10:35

    For the benefit of readers I'm going to divide this to 2 Sections

    Section A: Problem & how to handle similar scenarios

    Section B: Problem recreation & Solution

    Section A: Problem

    When this problem happens - process appears in task manager, then after 2-3sec disappears (its fine), then it waits for timeout and then exception is thrown System.InvalidOperationException: Process must exit before requested information can be determined.

    & See Scenario 4 below

    In your code:

    1. Process.WaitForExit(ProcessTimeOutMiliseconds); With this you're waiting for Process to Timeout or Exit, which ever takes place first.
    2. OutputWaitHandle.WaitOne(ProcessTimeOutMiliseconds)anderrorWaitHandle.WaitOne(ProcessTimeOutMiliseconds); With this you're waiting for OutputData & ErrorData stream read operation to signal its complete
    3. Process.ExitCode == 0 Gets status of process when it exited

    Different settings & their caveats:

    • Scenario 1 (Happy Path): Process completes before the timeout, and thus your stdoutput and stderror also finishes before it and all is well.
    • Scenario 2: Process, OutputWaitHandle & ErrorWaitHandle timesout however stdoutput & stderror is still being read and completes after time out WaitHandlers. This leads to another exception ObjectDisposedException()
    • Scenario 3: Process times-out first (19 sec) but stdout and stderror is in action, you wait for WaitHandler's to times out (19 sec), causing a added delay of + 19sec.
    • Scenario 4: Process times out and code attempts to prematurely query Process.ExitCode resulting in the error System.InvalidOperationException: Process must exit before requested information can be determined.

    I have tested this scenario over a dozen times and works fine, following settings have been used while testing

    • Size of Output stream ranging from 5KB to 198KB by initiating build of about 2-15 projects
    • Premature timeouts & process exits within the timeout window


    Updated Code

    .
    .
    .
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();
    
        //First waiting for ReadOperations to Timeout and then check Process to Timeout
        if (!outputWaitHandle.WaitOne(ProcessTimeOutMiliseconds) && !errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds)
            && !process.WaitForExit(ProcessTimeOutMiliseconds)  )
        {
            //To cancel the Read operation if the process is stil reading after the timeout this will prevent ObjectDisposeException
            process.CancelOutputRead();
            process.CancelErrorRead();
    
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Timed Out");
            Logs = output + Environment.NewLine + error;
           //To release allocated resource for the Process
            process.Close();
            return  (false, logs);
        }
    
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Completed On Time");
        Logs = output + Environment.NewLine + error;
        ExitCode = process.ExitCode.ToString();
        // Close frees the memory allocated to the exited process
        process.Close();
    
        //ExitCode now accessible
        return process.ExitCode == 0 ? (true, logs) : (false, logs);
        }
    }
    finally{}
    
    

    EDIT:

    After hours of playing around with MSBuild I was finally able to reproduce the issue at my system


    Section B: Problem Recreation & Solution

    MSBuild has -m[:number] switch which is used to specify the maximum number of concurrent processes to use when building.

    When this is enabled, MSBuild spawns a number of nodes that lives on even after the Build is complete. Now, Process.WaitForExit(milliseconds) would wait never exit and eventually timeout

    I was able to solve this in couple of ways

    • Spawn MSBuild process indirectly through CMD

      $path1 = """C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"" ""C:\Users\John\source\repos\Test\Test.sln"" -maxcpucount:3"
      $cmdOutput = cmd.exe /c $path1  '2>&1'
      $cmdOutput
      
    • Continue to use MSBuild but be sure to set the nodeReuse to False

      $filepath = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"
      $arg1 = "C:\Users\John\source\repos\Test\Test.sln"
      $arg2 = "-m:3"
      $arg3 = "-nr:False"
      
      Start-Process -FilePath $filepath -ArgumentList $arg1,$arg2,$arg3 -Wait -NoNewWindow
      
    • Even If parallel build is not enabled, you could still prevent your process from hanging at WaitForExit by launching the Build via CMD & therefore you do not create a direct dependency on Build process

      $path1 = """C:\....\15.0\Bin\MSBuild.exe"" ""C:\Users\John\source\Test.sln"""
      $cmdOutput = cmd.exe /c $path1  '2>&1'
      $cmdOutput
      

    The 2nd approach is preferred since you do not want too many MSBuild nodes to be lying around.

提交回复
热议问题