Quá trình đôi khi bị treo trong khi chờ Thoát


13

Điều gì có thể là lý do của quá trình của tôi treo trong khi chờ thoát?

Mã này phải khởi động tập lệnh powershell, bên trong thực hiện nhiều hành động, ví dụ: bắt đầu biên dịch lại mã qua MSBuild, nhưng có lẽ vấn đề là nó tạo ra quá nhiều đầu ra và mã này bị kẹt trong khi chờ thoát ngay cả khi tập lệnh shell power được thực thi chính xác

nó hơi "lạ" vì đôi khi mã này hoạt động tốt và đôi khi nó bị kẹt.

Mã treo tại:

process.WaitForExit (ProcessTimeOutMiliseconds);

Kịch bản Powershell thực thi trong khoảng 1-2 giây trong khi thời gian chờ là 19 giây.

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

Kịch bản:

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

Ở đâu

$args[0] = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe"

Biên tập

Để giải pháp của @ ingen, tôi đã thêm trình bao bọc nhỏ thử lại để thực thi MS Build bị treo

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

Trong trường hợp InternalExecuteScriptlà mã ingen của


tại dòng nào thực sự quá trình treo? và giới thiệu mã của bạn nhiều hơn nữa
— Mr.AF

@ Mr.AF bạn đúng - xong.
— Jo ERIC

1
Cuộc gọi thực tế của Powershell là một điều, tuy nhiên những gì bạn KHÔNG cung cấp là phần còn lại thực sự của tập lệnh mà bạn đang cố xử lý trong khi VỚIIN Powershell. Gọi powershell chính nó không phải là vấn đề, nhưng trong những gì bạn đang cố gắng làm. Chỉnh sửa bài đăng của bạn và thực hiện cuộc gọi / lệnh rõ ràng mà bạn đang cố thực hiện.
— DRapp

1
Nó thực sự kỳ lạ tôi đã cố gắng sao chép lỗi. Nó đã xảy ra ngẫu nhiên hai lần trong 20 lần thử hoặc một cái gì đó và tôi không thể kích hoạt lại.
— KiKoS

1
@Jo ERIC, ồ thú vị, bạn có nói rằng Rxcách tiếp cận đó có hiệu quả (như trong thời gian không hết thời gian) ngay cả với quá trình MSBuild đi lạc nằm xung quanh dẫn đến sự chờ đợi vô thời hạn? quan tâm để biết làm thế nào đã được xử lý
— Clint

Câu trả lời:


9

Hãy bắt đầu với một bản tóm tắt của câu trả lời được chấp nhận trong một bài liên quan.

Vấn đề là nếu bạn chuyển hướng StandardOutput và / hoặc StandardError, bộ đệm nội bộ có thể trở nên đầy đủ. Bất cứ thứ tự nào bạn sử dụng, có thể có một vấn đề:

  • Nếu bạn đợi quá trình thoát ra trước khi đọc StandardOutput, quy trình có thể chặn việc cố gắng ghi vào nó, vì vậy quá trình không bao giờ kết thúc.
  • Nếu bạn đọc từ StandardOutput bằng ReadToEnd thì quy trình của bạn có thể chặn nếu quy trình không bao giờ đóng StandardOutput (ví dụ nếu nó không bao giờ chấm dứt hoặc nếu nó bị chặn ghi vào StandardError).

Ngay cả câu trả lời được chấp nhận, tuy nhiên, đấu tranh với thứ tự thực hiện trong một số trường hợp nhất định.

EDIT: Xem câu trả lời bên dưới để biết cách tránh ObjectDisposedException nếu thời gian chờ xảy ra.

Chính trong những tình huống như thế này, nơi bạn muốn dàn dựng một vài sự kiện, Rx thực sự tỏa sáng.

Lưu ý rằng việc triển khai .NET của Rx có sẵn dưới dạng gói System.Reactive NuGet.

Hãy đi sâu vào để xem Rx tạo điều kiện làm việc với các sự kiện như thế nào.

// Subscribe to OutputData
Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.OutputDataReceived))
    .Subscribe(
        eventPattern => output.AppendLine(eventPattern.EventArgs.Data),
        exception => error.AppendLine(exception.Message)
    ).DisposeWith(disposables);

FromEventPatterncho phép chúng tôi ánh xạ các lần xuất hiện khác nhau của một sự kiện thành một luồng thống nhất (hay còn gọi là có thể quan sát được). Điều này cho phép chúng tôi xử lý các sự kiện trong một đường ống dẫn (với ngữ nghĩa giống như LINQ). Quá Subscribetải được sử dụng ở đây được cung cấp với một Action<EventPattern<...>>và một Action<Exception>. Bất cứ khi nào sự kiện quan sát được nêu ra, nó sendervà argssẽ được bao bọc EventPatternvà đẩy qua Action<EventPattern<...>>. Khi một ngoại lệ được đưa ra trong đường ống, Action<Exception>được sử dụng.

Một trong những nhược điểm của Eventmẫu, được minh họa rõ ràng trong trường hợp sử dụng này (và bởi tất cả các cách giải quyết trong bài được tham chiếu), là không rõ ràng khi nào / nơi để hủy đăng ký xử lý sự kiện.

Với Rx, chúng tôi nhận lại IDisposablekhi chúng tôi đăng ký. Khi chúng tôi loại bỏ nó, chúng tôi kết thúc đăng ký một cách hiệu quả. Với việc bổ sung DisposeWithphương thức mở rộng (mượn từ RxUI ), chúng ta có thể thêm nhiều IDisposables vào một CompositeDisposable(có tên disposablestrong các mẫu mã). Khi hoàn tất, chúng tôi có thể kết thúc tất cả các đăng ký bằng một cuộc gọi đến disposables.Dispose().

Để chắc chắn, không có gì chúng tôi có thể làm với Rx, mà chúng tôi sẽ không thể làm với vanilla .NET. Mã kết quả dễ dàng hơn rất nhiều để lý do, một khi bạn đã thích nghi với lối suy nghĩ chức năng.

public static void ExecuteScriptRx(string path, int processTimeOutMilliseconds, out string logs, out bool success, params string[] args)
{
    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();

    using (var process = new Process())
    using (var disposables = new CompositeDisposable())
    {
        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}'");

        // Raise the Process.Exited event when the process terminates.
        process.EnableRaisingEvents = true;

        // Subscribe to OutputData
        Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.OutputDataReceived))
            .Subscribe(
                eventPattern => output.AppendLine(eventPattern.EventArgs.Data),
                exception => error.AppendLine(exception.Message)
            ).DisposeWith(disposables);

        // Subscribe to ErrorData
        Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.ErrorDataReceived))
            .Subscribe(
                eventPattern => error.AppendLine(eventPattern.EventArgs.Data),
                exception => error.AppendLine(exception.Message)
            ).DisposeWith(disposables);

        var processExited =
            // Observable will tick when the process has gracefully exited.
            Observable.FromEventPattern<EventArgs>(process, nameof(Process.Exited))
                // First two lines to tick true when the process has gracefully exited and false when it has timed out.
                .Select(_ => true)
                .Timeout(TimeSpan.FromMilliseconds(processTimeOutMilliseconds), Observable.Return(false))
                // Force termination when the process timed out
                .Do(exitedSuccessfully => { if (!exitedSuccessfully) { try { process.Kill(); } catch {} } } );

        // Subscribe to the Process.Exited event.
        processExited
            .Subscribe()
            .DisposeWith(disposables);

        // Start process(ing)
        process.Start();

        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        // Wait for the process to terminate (gracefully or forced)
        processExited.Take(1).Wait();

        logs = output + Environment.NewLine + error;
        success = process.ExitCode == 0;
    }
}

Chúng tôi đã thảo luận về phần đầu tiên, nơi chúng tôi ánh xạ các sự kiện của mình đến các đài quan sát, vì vậy chúng tôi có thể nhảy thẳng đến phần nhu mì. Ở đây chúng ta gán giá trị quan sát của chúng ta cho processExitedbiến, bởi vì chúng ta muốn sử dụng nó nhiều lần.

Đầu tiên, khi chúng ta kích hoạt nó, bằng cách gọi Subscribe. Và sau này khi chúng ta muốn 'chờ đợi' giá trị đầu tiên của nó.

var processExited =
    // Observable will tick when the process has gracefully exited.
    Observable.FromEventPattern<EventArgs>(process, nameof(Process.Exited))
        // First two lines to tick true when the process has gracefully exited and false when it has timed out.
        .Select(_ => true)
        .Timeout(TimeSpan.FromMilliseconds(processTimeOutMilliseconds), Observable.Return(false))
        // Force termination when the process timed out
        .Do(exitedSuccessfully => { if (!exitedSuccessfully) { try { process.Kill(); } catch {} } } );

// Subscribe to the Process.Exited event.
processExited
    .Subscribe()
    .DisposeWith(disposables);

// Start process(ing)
...

// Wait for the process to terminate (gracefully or forced)
processExited.Take(1).Wait();

Một trong những vấn đề với OP là nó giả định process.WaitForExit(processTimeOutMiliseconds)sẽ chấm dứt quá trình khi hết thời gian. Từ MSDN :

Hướng dẫn thành phần Process chờ số mili giây đã chỉ định để quá trình liên quan thoát ra.

Thay vào đó, khi hết thời gian, nó chỉ trả lại quyền điều khiển cho luồng hiện tại (tức là nó dừng chặn). Bạn cần buộc chấm dứt thủ công khi quá trình hết thời gian. Để biết khi nào thời gian chờ đã xảy ra, chúng ta có thể ánh xạ Process.Exitedsự kiện đến mức processExitedcó thể quan sát được để xử lý. Bằng cách này chúng ta có thể chuẩn bị đầu vào cho Dotoán tử.

Các mã là khá tự giải thích. Nếu exitedSuccessfullyquá trình sẽ chấm dứt duyên dáng. Nếu không exitedSuccessfully, chấm dứt sẽ cần phải được buộc. Lưu ý rằng process.Kill()được thực hiện không đồng bộ, nhận xét ref . Tuy nhiên, gọi process.WaitForExit()ngay sau đó sẽ mở ra khả năng cho sự bế tắc một lần nữa. Vì vậy, ngay cả trong trường hợp buộc phải chấm dứt, tốt hơn hết là hãy để tất cả các mục tiêu được dọn sạch khi usingphạm vi kết thúc, vì dù sao đầu ra có thể bị coi là bị gián đoạn / bị hỏng.

Cấu try catchtrúc được dành riêng cho trường hợp đặc biệt (không có ý định chơi chữ) trong đó bạn đã căn chỉnh processTimeOutMillisecondsvới thời gian thực tế mà quá trình cần để hoàn thành. Nói cách khác, một điều kiện cuộc đua xảy ra giữa Process.Exitedsự kiện và bộ đếm thời gian. Khả năng xảy ra điều này một lần nữa được phóng đại bởi bản chất không đồng bộ của process.Kill(). Tôi đã gặp nó một lần trong khi thử nghiệm.


Để hoàn thiện, DisposeWithphương pháp mở rộng.

/// <summary>
/// Extension methods associated with the IDisposable interface.
/// </summary>
public static class DisposableExtensions
{
    /// <summary>
    /// Ensures the provided disposable is disposed with the specified <see cref="CompositeDisposable"/>.
    /// </summary>
    public static T DisposeWith<T>(this T item, CompositeDisposable compositeDisposable)
        where T : IDisposable
    {
        if (compositeDisposable == null)
        {
            throw new ArgumentNullException(nameof(compositeDisposable));
        }

        compositeDisposable.Add(item);
        return item;
    }
}

4
IMHO, chắc chắn có giá trị tiền thưởng. Câu trả lời hay, và phần giới thiệu đẹp về chủ đề cho RX.
— quetzalcoatl

Cảm ơn!!! ExecuteScriptRxTay cầm của bạn hangshoàn hảo. Việc treo máy không may vẫn xảy ra, nhưng tôi chỉ cần thêm trình bao bọc nhỏ vào ExecuteScriptRxbiểu diễn của bạn Retryvà sau đó nó thực thi tốt. Lý do bị treo MSBUILD có thể là câu trả lời @Clint. Tái bút: Mã đó khiến tôi cảm thấy ngu ngốc <lol> Đó là lần đầu tiên tôi nhìn thấySystem.Reactive.Linq;
— Jo mẹo

Mã của
— Wrapperer

3

Vì lợi ích của độc giả, tôi sẽ chia phần này thành 2 Phần

Phần A: Vấn đề & cách xử lý các tình huống tương tự

Phần B: Giải pháp & Giải pháp cho vấn đề

Mục A: Vấn đề

Khi sự cố này xảy ra - quá trình xuất hiện trong trình quản lý tác vụ, sau đó sau 2-3 giây biến mất (nó vẫn ổn), sau đó nó chờ thời gian chờ và ngoại lệ được ném System.InvalidOperationException: Quá trình phải thoát trước khi có thể xác định thông tin được yêu cầu.

& Xem Kịch bản 4 bên dưới

Trong mã của bạn:

  1. Process.WaitForExit(ProcessTimeOutMiliseconds); Với điều này bạn đang chờ đợi Processđể Timeout hoặc Exit , trong đó bao giờ diễn ra đầu tiên .
  2. OutputWaitHandle.WaitOne(ProcessTimeOutMiliseconds)và errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds); với điều này, bạn đang chờ đợi OutputData& ErrorDatatruyền phát hoạt động đọc để báo hiệu sự hoàn thành của nó
  3. Process.ExitCode == 0 Có trạng thái của quá trình khi nó thoát

Cài đặt khác nhau và hãy cẩn thận:

  • Kịch bản 1 (Đường dẫn hạnh phúc) : Quá trình hoàn tất trước khi hết thời gian, và do đó, thiết bị xuất chuẩn và thiết bị xuất chuẩn của bạn cũng kết thúc trước khi nó và tất cả đều ổn.
  • Kịch bản 2 : Quá trình, Đầu raWaitHandle & ErrorWaitHandle hết thời gian, tuy nhiên tiêu chuẩn & stderror vẫn đang được đọc và hoàn thành sau khi hết thời gian WaitHandlers. Điều này dẫn đến một ngoại lệ khácObjectDisposedException()
  • Kịch bản 3 : Quá trình hết thời gian đầu tiên (19 giây) nhưng thiết bị xuất chuẩn và stderror đang hoạt động, bạn đợi WaitHandler hết thời gian (19 giây), gây thêm độ trễ + 19 giây.
  • Kịch bản 4 : Xử lý hết thời gian và mã cố truy vấn sớm Process.ExitCodedẫn đến lỗi System.InvalidOperationException: Process must exit before requested information can be determined.

Tôi đã thử nghiệm kịch bản này hơn một chục lần và hoạt động tốt, các cài đặt sau đã được sử dụng trong khi thử nghiệm

  • Kích thước của luồng đầu ra từ 5KB đến 198KB bằng cách bắt đầu xây dựng khoảng 2-15 dự án
  • Hết thời gian chờ & quá trình thoát sớm trong cửa sổ hết thời gian


Mã cập nhật

.
.
.
    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{}

BIÊN TẬP:

Sau nhiều giờ chơi với MSBuild, cuối cùng tôi cũng có thể tái tạo vấn đề tại hệ thống của mình


Phần B: Giải pháp & Giải pháp Vấn đề

MSBuild có-m[:number]công tắc được sử dụng để chỉ định số lượng quy trình đồng thời tối đa sẽ sử dụng khi xây dựng.

Khi điều này được kích hoạt, MSBuild sẽ sinh ra một số nút tồn tại ngay cả sau khi Build hoàn tất. Bây giờ, Process.WaitForExit(milliseconds)sẽ chờ không bao giờ thoát và cuối cùng là hết thời gian

Tôi đã có thể giải quyết điều này theo nhiều cách

  • Quá trình sinh sản MSBuild gián tiếp thông qua 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
  • Tiếp tục sử dụng MSBuild nhưng hãy chắc chắn đặt nútReuse thành Sai

    $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
  • Ngay cả khi xây dựng song song không được bật, bạn vẫn có thể ngăn quá trình của mình bị treo WaitForExitbằng cách khởi chạy Build qua CMD và do đó bạn không tạo ra sự phụ thuộc trực tiếp vào quá trình Xây dựng

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

Cách tiếp cận thứ 2 được ưa thích vì bạn không muốn có quá nhiều nút MSBuild nằm xung quanh.


Vì vậy, như tôi đã nói ở trên, cảm ơn, điều này "-nr:False","-m:3"dường như đã sửa lỗi hành vi hang-ish của MSBuild, với Rx solutiontoàn bộ quá trình có phần đáng tin cậy (thời gian sẽ hiển thị). Tôi ước tôi có thể chấp nhận cả hai câu trả lời hoặc đưa ra hai tiền thưởng
— Jo ERIC

@Jo ERIC Tôi chỉ đang cố gắng để biết liệu Rxcách tiếp cận trong giải pháp khác có thể giải quyết vấn đề mà không cần áp dụng hay không -nr:False" ,"-m:3". Theo hiểu biết của tôi, nó xử lý sự chờ đợi vô thời hạn từ những bế tắc và những thứ khác mà tôi đã đề cập trong phần 1. Và nguyên nhân gốc rễ trong Phần 2 là điều tôi tin là nguyên nhân sâu xa của vấn đề mà bạn gặp phải;) Tôi có thể sai đó là lý do tại sao Tôi hỏi, chỉ có thời gian mới trả lời ... Chúc mừng !!
— Clint

3

Vấn đề là nếu bạn chuyển hướng StandardOutput và / hoặc StandardError, bộ đệm nội bộ có thể trở nên đầy đủ.

Để giải quyết các vấn đề đã nói ở trên, bạn có thể chạy quy trình trong các luồng riêng biệt. Tôi không sử dụng WaitForExit, tôi sử dụng sự kiện thoát quy trình sẽ trả về Mã thoát của quy trình không đồng bộ để đảm bảo nó đã hoàn thành.

public async Task<int> RunProcessAsync(params string[] args)
    {
        try
        {
            var tcs = new TaskCompletionSource<int>();

            var process = new Process
            {
                StartInfo = {
                    FileName = 'file path',
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    Arguments = "shell command",
                    UseShellExecute = false,
                    CreateNoWindow = true
                },
                EnableRaisingEvents = true
            };


            process.Exited += (sender, args) =>
            {
                tcs.SetResult(process.ExitCode);
                process.Dispose();
            };

            process.Start();
            // Use asynchronous read operations on at least one of the streams.
            // Reading both streams synchronously would generate another deadlock.
            process.BeginOutputReadLine();
            string tmpErrorOut = await process.StandardError.ReadToEndAsync();
            //process.WaitForExit();


            return await tcs.Task;
        }
        catch (Exception ee) {
            Console.WriteLine(ee.Message);
        }
        return -1;
    }

Đoạn mã trên được thử nghiệm bằng cách gọi FFMPEG.exe với các đối số dòng lệnh. Tôi đã chuyển đổi tập tin mp4 thành tập tin mp3 và thực hiện hơn 1000 video cùng một lúc mà không thất bại. Thật không may, tôi không có kinh nghiệm vỏ điện trực tiếp nhưng hy vọng điều này sẽ giúp.


Mã này thật kỳ lạ, tương tự như các giải pháp khác đã thất bại (bị mắc kẹt) trong lần thử đầu tiên và sau đó dường như hoạt động tốt (như 5 lần thử khác, tôi sẽ kiểm tra thêm). Btw tại sao bạn thực hiện BegingOutputReadlinevà sau đó thực hiện ReadToEndAsynctrên StandardError?
— Jo ERIC

OP đã đọc không đồng bộ, do đó không chắc là bế tắc trên bộ đệm giao diện điều khiển là vấn đề ở đây.
— Yaakov

0

Không chắc đây có phải là vấn đề của bạn không, nhưng nhìn vào MSDN dường như có một số điều kỳ lạ với WaitForExit bị quá tải khi bạn chuyển hướng đầu ra không đồng bộ. Bài viết MSDN khuyên bạn nên gọi WaitForExit không có đối số sau khi gọi phương thức quá tải.

Trang tài liệu nằm ở đây. Văn bản có liên quan:

Khi đầu ra tiêu chuẩn đã được chuyển hướng đến các trình xử lý sự kiện không đồng bộ, có thể việc xử lý đầu ra sẽ không hoàn thành khi phương thức này trả về. Để đảm bảo rằng việc xử lý sự kiện không đồng bộ đã được hoàn thành, hãy gọi quá tải WaitForExit () không có tham số nào sau khi nhận được đúng từ quá tải này. Để giúp đảm bảo rằng sự kiện Đã thoát được xử lý chính xác trong các ứng dụng Windows Forms, hãy đặt thuộc tính SyncizingObject.

Sửa đổi mã có thể trông giống như thế này:

if (process.WaitForExit(ProcessTimeOutMiliseconds))
{
  process.WaitForExit();
}

Có một số điều phức tạp với việc sử dụng process.WaitForExit()như được chỉ ra bởi các ý kiến ​​cho câu trả lời này .
— ingen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.