Co może być przyczyną zawieszania się procesu podczas oczekiwania na wyjście?
Ten kod musi uruchomić skrypt PowerShell, który wykonuje wiele akcji, np. Rozpocząć rekompilację kodu za pomocą MSBuild, ale prawdopodobnie problem polega na tym, że generuje on zbyt wiele danych wyjściowych i kod ten blokuje się podczas oczekiwania na zakończenie, nawet po prawidłowym wykonaniu skryptu powłoki
to trochę „dziwne”, ponieważ czasami ten kod działa dobrze, a czasem po prostu się zacina.
Kod zawiesza się w:
process.WaitForExit (ProcessTimeOutMiliseconds);
Skrypt PowerShell wykonuje się w ciągu 1–2 sekund, a limit czasu wynosi 19 sekund.
public static (bool Success, string Logs) ExecuteScript(string path, int ProcessTimeOutMiliseconds, params string[] args)
{
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
using (var outputWaitHandle = new AutoResetEvent(false))
using (var errorWaitHandle = new AutoResetEvent(false))
{
try
{
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "powershell.exe",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
Arguments = $"-ExecutionPolicy Bypass -File \"{path}\"",
WorkingDirectory = Path.GetDirectoryName(path)
};
if (args.Length > 0)
{
var arguments = string.Join(" ", args.Select(x => $"\"{x}\""));
process.StartInfo.Arguments += $" {arguments}";
}
output.AppendLine($"args:'{process.StartInfo.Arguments}'");
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
output.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorWaitHandle.Set();
}
else
{
error.AppendLine(e.Data);
}
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(ProcessTimeOutMiliseconds);
var logs = output + Environment.NewLine + error;
return process.ExitCode == 0 ? (true, logs) : (false, logs);
}
}
finally
{
outputWaitHandle.WaitOne(ProcessTimeOutMiliseconds);
errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds);
}
}
}
Scenariusz:
start-process $args[0] App.csproj -Wait -NoNewWindow
[string]$sourceDirectory = "\bin\Debug\*"
[int]$count = (dir $sourceDirectory | measure).Count;
If ($count -eq 0)
{
exit 1;
}
Else
{
exit 0;
}
gdzie
$args[0] = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe"
Edytować
Do rozwiązania @ ingen dodałem małe opakowanie, które próbuje wykonać powieszony MS Build
public static void ExecuteScriptRx(string path, int processTimeOutMilliseconds, out string logs, out bool success, params string[] args)
{
var current = 0;
int attempts_count = 5;
bool _local_success = false;
string _local_logs = "";
while (attempts_count > 0 && _local_success == false)
{
Console.WriteLine($"Attempt: {++current}");
InternalExecuteScript(path, processTimeOutMilliseconds, out _local_logs, out _local_success, args);
attempts_count--;
}
success = _local_success;
logs = _local_logs;
}
Gdzie InternalExecuteScriptjest kod ingen
Rxpodejście zadziałało (bo w nim nie przekroczyło limitu czasu) nawet w przypadku nieudanego procesu MSBuild, który prowadzi do nieokreślonego oczekiwania? zainteresowany wiedzieć, jak to było obsługiwane