Apa yang mungkin menjadi alasan proses saya menggantung sambil menunggu keluar?
Kode ini harus memulai skrip PowerShell yang di dalamnya melakukan banyak tindakan misalnya mulai mengkompilasi ulang kode melalui MSBuild, tetapi mungkin masalahnya adalah ia menghasilkan terlalu banyak output dan kode ini macet saat menunggu untuk keluar bahkan setelah skrip shell daya dijalankan dengan benar
itu agak "aneh" karena kadang-kadang kode ini berfungsi dengan baik dan kadang-kadang hanya macet.
Kode hang di:
process.WaitForExit (ProcessTimeOutMiliseconds);
Script Powershell dieksekusi dalam 1-2sec sementara itu timeout adalah 19sec.
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);
}
}
}
Naskah:
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;
}
dimana
$args[0] = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe"
Edit
Untuk solusi @ ingen saya menambahkan pembungkus kecil yang mencoba untuk mengeksekusi MS Build yang digantung
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;
}
Di mana InternalExecuteScript
kode asli
Rx
pendekatan bekerja (seperti di dalamnya tidak ada batas waktu) bahkan dengan proses MSBuild liar tergeletak mengarah ke penantian tanpa batas waktu? tertarik untuk mengetahui bagaimana itu ditanganiJawaban:
Mari kita mulai dengan rekap jawaban yang diterima di pos terkait.
Bahkan jawaban yang diterima, bagaimanapun, bergumul dengan urutan eksekusi dalam kasus-kasus tertentu.
Dalam situasi seperti ini, di mana Anda ingin mengatur beberapa acara, Rx benar-benar bersinar.
Perhatikan .NET implementasi Rx tersedia sebagai paket NuGet System.Reactive.
Mari selami untuk melihat bagaimana Rx memfasilitasi bekerja dengan acara.
FromEventPattern
memungkinkan kita untuk memetakan kejadian yang berbeda dari suatu peristiwa ke aliran yang disatukan (alias dapat diamati). Hal ini memungkinkan kami untuk menangani acara dalam pipa (dengan semantik seperti LINQ). TheSubscribe
kelebihan yang digunakan di sini disediakan denganAction<EventPattern<...>>
danAction<Exception>
. Setiap kali acara yang diamati dinaikkan, itusender
danargs
akan dibungkus olehEventPattern
dan didorong melaluiAction<EventPattern<...>>
. Saat eksepsi dimunculkan dalam pipa,Action<Exception>
digunakan.Salah satu kelemahan dari
Event
pola, dengan jelas diilustrasikan dalam kasus penggunaan ini (dan oleh semua solusi di pos yang dirujuk), adalah bahwa tidak jelas kapan / di mana harus berhenti berlangganan event handler.Dengan Rx kita mendapatkan kembali
IDisposable
saat kita membuat langganan. Ketika kami membuangnya, kami secara efektif mengakhiri langganan. Dengan penambahanDisposeWith
metode ekstensi (dipinjam dari RxUI ), kita dapat menambahkan beberapaIDisposable
s keCompositeDisposable
(dinamaidisposables
dalam contoh kode). Setelah selesai, kami dapat mengakhiri semua langganan dengan satu panggilandisposables.Dispose()
.Yang pasti, tidak ada yang bisa kita lakukan dengan Rx, yang tidak bisa kita lakukan dengan vanilla .NET. Kode yang dihasilkan jauh lebih mudah untuk dipikirkan, setelah Anda beradaptasi dengan cara berpikir fungsional.
Kita sudah membahas bagian pertama, di mana kita memetakan acara kita ke yang bisa diamati, sehingga kita bisa langsung melompat ke bagian yang gemuk. Di sini kami menetapkan kami diamati untuk
processExited
variabel yang , karena kita ingin menggunakannya lebih dari sekali.Pertama, ketika kita mengaktifkannya, dengan menelepon
Subscribe
. Dan nanti ketika kita ingin 'menunggu' nilai pertamanya.Salah satu masalah dengan OP adalah mengasumsikan
process.WaitForExit(processTimeOutMiliseconds)
akan menghentikan proses ketika habis. Dari MSDN :Sebaliknya, ketika waktu habis, ia hanya mengembalikan kontrol ke utas saat ini (yaitu berhenti memblokir). Anda harus memaksakan penghentian secara manual saat proses berakhir. Untuk mengetahui kapan time out telah terjadi, kita dapat memetakan
Process.Exited
acara tersebut ke yangprocessExited
dapat diamati untuk diproses. Dengan cara ini kita dapat menyiapkan input untukDo
operator.Kode ini cukup jelas. Jika
exitedSuccessfully
prosesnya akan berakhir dengan anggun. Jika tidakexitedSuccessfully
, pemutusan hubungan kerja harus dipaksakan. Catatan yangprocess.Kill()
dijalankan secara tidak serempak, komentar ref . Namun, panggilanprocess.WaitForExit()
tepat setelah akan membuka kemungkinan kebuntuan lagi. Jadi, bahkan dalam kasus pemutusan paksa, lebih baik membiarkan semua sekali pakai dibersihkan ketikausing
ruang lingkup berakhir, karena output tetap dapat dianggap terganggu / rusak.The
try catch
membangun dicadangkan untuk kasus luar biasa (no pun intended) di mana Anda sudah selarasprocessTimeOutMilliseconds
dengan waktu yang sebenarnya dibutuhkan oleh proses untuk selesai. Dengan kata lain, kondisi lomba terjadi antaraProcess.Exited
acara dan timer. Kemungkinan ini terjadi sekali lagi diperbesar oleh sifat asinkronprocess.Kill()
. Saya pernah melihatnya sekali selama pengujian.Untuk kelengkapan,
DisposeWith
metode ekstensi.sumber
ExecuteScriptRx
Pegangan Andahangs
sempurna. Sayangnya hang masih terjadi, tapi saya baru saja menambahkan pembungkus kecil di atas AndaExecuteScriptRx
yang melakukanRetry
dan kemudian dijalankan dengan baik. Alasan MSBUILD hang mungkin adalah jawaban @Clint. PS: Kode itu membuat saya merasa bodoh <lol> Itu pertama kali saya lihatSystem.Reactive.Linq;
Demi kepentingan pembaca, saya akan membagi ini menjadi 2 Bagian
Bagian A: Masalah & bagaimana menangani skenario serupa
Bagian B: Rekreasi masalah & Solusi
Bagian A: Masalah
Dalam kode Anda:
Process.WaitForExit(ProcessTimeOutMiliseconds);
Dengan ini Anda sedang menungguProcess
untuk Timeout atau Exit , yang pernah terjadi pertama .OutputWaitHandle.WaitOne(ProcessTimeOutMiliseconds)
danerrorWaitHandle.WaitOne(ProcessTimeOutMiliseconds);
Dengan ini Anda menungguOutputData
&ErrorData
streaming membaca operasi untuk mengisinya lengkapProcess.ExitCode == 0
Mendapat status proses saat keluarPengaturan berbeda & peringatan mereka:
ObjectDisposedException()
Process.ExitCode
menghasilkan kesalahanSystem.InvalidOperationException: Process must exit before requested information can be determined
.Saya telah menguji skenario ini lebih dari selusin kali dan berfungsi dengan baik, pengaturan berikut telah digunakan saat pengujian
Kode yang Diperbarui
EDIT:
Setelah berjam-jam bermain-main dengan MSBuild saya akhirnya bisa mereproduksi masalah di sistem saya
Bagian B: Rekreasi Masalah & Solusi
Saya bisa menyelesaikan ini dalam beberapa cara
Menelurkan proses MSBuild secara tidak langsung melalui CMD
Terus menggunakan MSBuild tetapi pastikan untuk mengatur nodeReuse ke False
Bahkan jika build paralel tidak diaktifkan, Anda masih bisa mencegah proses Anda menggantung
WaitForExit
dengan meluncurkan Build via CMD & karena itu Anda tidak membuat ketergantungan langsung pada proses BuildPendekatan 2 lebih disukai karena Anda tidak ingin terlalu banyak node MSBuild untuk berbaring.
sumber
"-nr:False","-m:3"
tampaknya telah memperbaiki perilaku hang-ish MSBuild, yang denganRx solution
membuat seluruh proses agak dapat diandalkan (waktu akan menunjukkan). Saya berharap saya bisa menerima kedua jawaban atau memberikan dua hadiahRx
pendekatan dalam solusi lain dapat menyelesaikan masalah tanpa menerapkan-nr:False" ,"-m:3"
. Dalam pemahaman saya ini menangani menunggu tidak pasti dari kebuntuan dan hal-hal lain yang telah saya bahas di bagian 1. Dan rootcause di Bagian 2 adalah apa yang saya yakini adalah akar penyebab masalah yang Anda hadapi;) Saya mungkin salah karena itu Saya bertanya, hanya waktu yang akan memberitahu ... Ceria !!Masalahnya adalah bahwa jika Anda mengarahkan StandardOutput dan / atau StandardError buffer internal dapat menjadi penuh.
Untuk mengatasi masalah yang disebutkan di atas, Anda dapat menjalankan proses di utas terpisah. Saya tidak menggunakan WaitForExit, saya menggunakan proses keluar dari acara yang akan mengembalikan ExitCode proses asynchronous memastikan telah selesai.
Kode di atas adalah pertempuran yang diuji memanggil FFMPEG.exe dengan argumen baris perintah. Saya mengubah file mp4 menjadi file mp3 dan melakukan lebih dari 1000 video sekaligus tanpa gagal. Sayangnya saya tidak memiliki pengalaman power shell langsung tetapi berharap ini bisa membantu.
sumber
BegingOutputReadline
dan kemudian melakukanReadToEndAsync
padaStandardError
?Tidak yakin apakah ini masalah Anda, tetapi melihat MSDN tampaknya ada keanehan dengan WaitForExit yang kelebihan beban saat Anda mengarahkan output secara tidak sinkron. Artikel MSDN merekomendasikan untuk memanggil WaitForExit yang tidak memerlukan argumen setelah memanggil metode kelebihan beban.
Halaman dokumen terletak di sini. Teks yang relevan:
Modifikasi kode mungkin terlihat seperti ini:
sumber
process.WaitForExit()
seperti yang ditunjukkan oleh komentar untuk jawaban ini .