बाहर निकलने के इंतजार में प्रक्रिया कभी-कभी लटक जाती है


13

बाहर निकलने के इंतजार के दौरान मेरी प्रक्रिया के लटकने का क्या कारण हो सकता है?

इस कोड को पॉवरशेल स्क्रिप्ट शुरू करनी होती है, जिसके अंदर कई कार्रवाई करता है। जैसे MSBuild के माध्यम से कोड को फिर से जमा करना शुरू करता है, लेकिन शायद समस्या यह है कि यह बहुत अधिक आउटपुट उत्पन्न करता है और पावर शेल स्क्रिप्ट को सही तरीके से निष्पादित करने के बाद भी बाहर निकलने के इंतजार में यह कोड अटक जाता है।

यह थोड़े "अजीब" है क्योंकि कभी-कभी यह कोड ठीक काम करता है और कभी-कभी यह बस अटक जाता है।

कोड हैंग हो जाता है:

process.WaitForExit (ProcessTimeOutMiliseconds);

Powershell script 1-2sec की तरह निष्पादित होती है, इस बीच का समय 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);
        }
    }
}

स्क्रिप्ट:

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

कहाँ पे

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

संपादित करें

@ Ingen के समाधान के लिए मैंने छोटे रैपर जोड़े जो एम.एस.

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

जहाँ InternalExecuteScriptकोड कूट है


वास्तव में किस रेखा पर प्रक्रिया लटकती है? और अपने कोड को बहुत अधिक
— पहचानें

@ Mr.AF आप सही हो - हो गया।
— जोएलिटी

1
पॉवरशेल की वास्तविक कॉलिंग एक बात है, हालांकि जो आप प्रदान नहीं कर रहे हैं वह वास्तविक बाकी स्क्रिप्ट है जिसे आप पॉवर्सशेल के दौरान संसाधित करने की कोशिश कर रहे हैं। शक्तियां कॉल करना स्वयं समस्या नहीं है, लेकिन आप जो करने की कोशिश कर रहे हैं उसके भीतर। अपनी पोस्ट संपादित करें और उस स्पष्ट कॉल / कमांड को डालें जिसे आप निष्पादित करने का प्रयास कर रहे हैं।
— DRapp

1
यह वास्तव में अजीब है मैंने त्रुटि को दोहराने की कोशिश की। यह 20 प्रयासों या कुछ पर दो बार अनियमित रूप से हुआ और मैं इसे फिर से ट्रिगर करने में असमर्थ हूं।
— KiKoS

1
@ जॉली, ओह कूल दिलचस्प है, क्या आप कह रहे हैं कि Rxकाम किया (जैसा कि इसमें कोई समय नहीं लगा था) यहां तक ​​कि आवारा MSBuild प्रक्रिया के साथ अनिश्चित प्रतीक्षा के लिए अग्रणी है? यह जानने की रुचि है कि कैसे संभाला गया
— क्लिंट

जवाबों:


9

आइए संबंधित पोस्ट में स्वीकृत उत्तर के पुनर्कथन के साथ शुरू करें ।

समस्या यह है कि यदि आप StandardOutput और / या StandardError को पुनर्निर्देशित करते हैं तो आंतरिक बफर पूर्ण हो सकता है। आप जो भी आदेश का उपयोग करते हैं, वहाँ एक समस्या हो सकती है:

  • यदि आप StandardOutput को पढ़ने से पहले प्रक्रिया से बाहर निकलने की प्रतीक्षा करते हैं तो यह प्रक्रिया उस पर लिखने की कोशिश को रोक सकती है, इसलिए प्रक्रिया कभी समाप्त नहीं होती है।
  • यदि आप ReadToEnd का उपयोग करते हुए StandardOutput से पढ़ते हैं तो आपकी प्रक्रिया ब्लॉक हो सकती है यदि प्रक्रिया कभी भी StandardOutput को बंद नहीं करती है (उदाहरण के लिए यदि यह कभी समाप्त नहीं होती है, या यदि यह StandardError के लिए लेखन अवरुद्ध है)।

हालांकि, स्वीकृत उत्तर भी, कुछ मामलों में निष्पादन के आदेश के साथ संघर्ष करता है।

संपादित करें: यदि टाइमआउट होता है तो किसी ObjectDisposedException से बचने के लिए नीचे दिए गए उत्तर देखें ।

यह इस तरह की स्थितियों में है, जहां आप कई घटनाओं को ऑर्केस्ट्रेट करना चाहते हैं, कि आरएक्स वास्तव में चमकता है।

ध्यान दें। Rx का .NET कार्यान्वयन System.Reactive NuGet पैकेज के रूप में उपलब्ध है।

आइए देखें कि कैसे घटनाओं के साथ काम करने में Rx की सुविधा होती है।

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

FromEventPatternहमें एक घटना के अलग-अलग घटनाओं को एक एकीकृत धारा (उर्फ अवलोकन योग्य) के मानचित्र में बदलने की अनुमति देता है। यह हमें एक पाइपलाइन में घटनाओं को संभालने की अनुमति देता है (LINQ- जैसे शब्दार्थ के साथ)। Subscribeयहां इस्तेमाल किया अधिभार एक साथ प्रदान की जाती है Action<EventPattern<...>>और एक Action<Exception>। जब भी देखी गई घटना को उठाया जाता है, तो इसकी senderऔर argsइसे लपेटकर EventPatternऔर अंदर धकेल दिया जाएगा Action<EventPattern<...>>। जब एक अपवाद पाइपलाइन में उठाया Action<Exception>जाता है , तो इसका उपयोग किया जाता है।

Eventपैटर्न की कमियों में से एक , इस उपयोग के मामले में स्पष्ट रूप से चित्रित किया गया है (और संदर्भित पोस्ट में सभी वर्कअराउंड्स), यह स्पष्ट नहीं है कि इवेंट हैंडलर को अनसब्सक्राइब करने के लिए कब / कहाँ है।

IDisposableजब हम सदस्यता लेते हैं तो Rx के साथ हम वापस आ जाते हैं । जब हम इसका निपटान करते हैं, तो हम प्रभावी रूप से सदस्यता समाप्त कर देते हैं। DisposeWithविस्तार विधि के अलावा ( RxUI से उधार ), हम IDisposableएक से CompositeDisposable( disposablesकोड नमूने में नाम ) कई जोड़ सकते हैं । जब हम सब कर लेंगे, हम एक कॉल के साथ सभी सदस्यता समाप्त कर सकते हैं disposables.Dispose()।

यह सुनिश्चित करने के लिए, हम Rx के साथ कुछ भी नहीं कर सकते हैं, कि हम वेनिला .NET के साथ नहीं कर पाएंगे। परिणामस्वरूप कोड के बारे में तर्क करना बहुत आसान है, एक बार जब आप सोच के कार्यात्मक तरीके से अनुकूलित हो जाते हैं।

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

हमने पहले भाग पर चर्चा की, जहां हम अपनी घटनाओं को वेधशालाओं में मैप करते हैं, इसलिए हम सीधे मांस वाले हिस्से में जा सकते हैं। यहां हम processExitedचर के लिए अपने अवलोकन को निर्दिष्ट करते हैं, क्योंकि हम इसे एक से अधिक बार उपयोग करना चाहते हैं।

सबसे पहले, जब हम इसे कॉल करके सक्रिय करते हैं Subscribe। और बाद में जब हम इसके पहले मूल्य का 'इंतजार' करना चाहते हैं।

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

ओपी के साथ समस्याओं में से एक यह है कि यह मान लेता है कि process.WaitForExit(processTimeOutMiliseconds)जब यह प्रक्रिया समाप्त हो जाएगी। से MSDN :

संबंधित प्रक्रिया से बाहर निकलने के लिए मिलीसेकंड की निर्दिष्ट संख्या की प्रतीक्षा करने के लिए प्रक्रिया घटक को निर्देश देता है ।

इसके बजाय, जब वह बाहर निकलता है, तो यह केवल वर्तमान थ्रेड पर नियंत्रण लौटाता है (यानी यह ब्लॉक करना बंद कर देता है)। प्रक्रिया समय समाप्त होने पर आपको मैन्युअल रूप से समाप्ति को बाध्य करने की आवश्यकता है। यह जानने के लिए कि कब समय समाप्त हो गया है, हम Process.Exitedघटना को processExitedप्रसंस्करण के लिए अवलोकन योग्य बना सकते हैं। इस तरह हम Doऑपरेटर के लिए इनपुट तैयार कर सकते हैं ।

कोड बहुत आत्म-व्याख्यात्मक है। अगर exitedSuccessfullyइस प्रक्रिया को इनायत से समाप्त कर दिया जाएगा। यदि नहीं exitedSuccessfully, तो समाप्ति को मजबूर होने की आवश्यकता होगी। ध्यान दें कि process.Kill()एसिंक्रोनस रूप से निष्पादित किया गया है, टिप्पणी से इनकार करें । हालाँकि, process.WaitForExit()सही कॉल करने के बाद फिर से गतिरोध की संभावना खुल जाएगी। इसलिए जबरन समाप्ति के मामले में भी, यह बेहतर है कि usingगुंजाइश समाप्त होने पर सभी डिस्पोजेबल को साफ किया जाए , क्योंकि आउटपुट को वैसे भी बाधित / भ्रष्ट माना जा सकता है।

try catchनिर्माण असाधारण मामला (कोई यमक इरादा) जहां गठबंधन किया है के लिए आरक्षित है processTimeOutMillisecondsपूरा करने के लिए प्रक्रिया की जरूरत वास्तविक समय के साथ। दूसरे शब्दों में, Process.Exitedघटना और टाइमर के बीच एक दौड़ की स्थिति होती है । ऐसा होने की संभावना फिर से अतुल्यकालिक प्रकृति द्वारा बढ़ाई जाती है process.Kill()। मैंने इसे एक बार परीक्षण के दौरान सामना किया है।


पूर्णता के लिए, DisposeWithविस्तार विधि।

/// <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, निश्चित रूप से इनाम के लायक है। अच्छा जवाब, और अच्छा विषय पर RX के लिए परिचय।
— quetzalcoatl

धन्यवाद!!! आपका ExecuteScriptRxहैंडल hangsपूरी तरह से। दुर्भाग्य से लटके हुए अभी भी होते हैं, लेकिन मैंने सिर्फ आपके ExecuteScriptRxप्रदर्शन पर छोटे आवरण को जोड़ा है Retryऔर फिर यह ठीक निष्पादित करता है। MSBUILD हैंग होने का कारण @ क्लिंट का जवाब हो सकता है। पुनश्च: उस कोड ने मुझे बेवकूफ बना दिया <lol> यह पहली बार है जब मैं देख रहा हूंSystem.Reactive.Linq;
— जोएल्टी

मुख्य पोस्ट में रैपर का कोड
— जोएल्टी

3

पाठकों के लाभ के लिए मैं इसे 2 खंडों में विभाजित करने जा रहा हूं

अनुभाग ए: समस्या और समान परिदृश्यों को कैसे संभालना है

अनुभाग बी: समस्या मनोरंजन और समाधान

अनुभाग ए: समस्या

जब यह समस्या होती है - कार्य प्रबंधक में प्रक्रिया दिखाई देती है, तो 2-3sec के गायब होने (उसके ठीक होने) के बाद, यह समय समाप्त होने की प्रतीक्षा करता है और फिर अपवाद को फेंक दिया जाता है। System.InvalidOperationException: प्रक्रिया को अनुरोधित जानकारी निर्धारित करने से पहले बाहर निकलना चाहिए।

& नीचे परिदृश्य 4 देखें

आपके कोड में:

  1. Process.WaitForExit(ProcessTimeOutMiliseconds); इस के साथ आप के लिए प्रतीक्षा कर रहे हैं Processकरने के लिए समय समाप्त या बाहर निकलें , जो कभी जगह लेता है पहले ।
  2. OutputWaitHandle.WaitOne(ProcessTimeOutMiliseconds)और errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds); इसके साथ आप प्रतीक्षा कर रहे हैं OutputData& ErrorDataस्ट्रीम को इसके पूर्ण सिग्नल के लिए ऑपरेशन पढ़ें
  3. Process.ExitCode == 0 बाहर निकलने पर प्रक्रिया की स्थिति बन जाती है

विभिन्न सेटिंग्स और उनके चेतावनी:

  • परिदृश्य 1 (हैप्पी पाथ) : प्रक्रिया समय समाप्त होने से पहले पूरी हो जाती है, और इस प्रकार आपका स्टडआउट और स्टडर भी इससे पहले खत्म हो जाता है और सब ठीक हो जाता है।
  • परिदृश्य 2 : प्रक्रिया, OutputWaitHandle और ErrorWaitHandle बार-बार हालांकि stdoutput & stderror अभी भी पढ़ी जा रही है और WaitHandlers को समय के बाद पूरा करती है। यह एक और अपवाद की ओर जाता हैObjectDisposedException()
  • परिदृश्य 3 : प्रोसेस टाइम-आउट पहले (19 सेकंड) लेकिन स्टडआउट और स्टडरर कार्रवाई में है, आप WaitHandler के कई बार (19 सेकंड) के लिए प्रतीक्षा करते हैं, जिससे + 19 सेकंड की अतिरिक्त देरी होती है।
  • परिदृश्य 4 : प्रक्रिया के समय और कोड को समय से पहले क्वेरी Process.ExitCodeमें त्रुटि के परिणामस्वरूप प्रयास करता है System.InvalidOperationException: Process must exit before requested information can be determined।

मैंने एक दर्जन से अधिक बार इस परिदृश्य का परीक्षण किया है और ठीक काम करता है, परीक्षण करते समय निम्नलिखित सेटिंग्स का उपयोग किया गया है

  • लगभग 2-15 परियोजनाओं के निर्माण की शुरुआत करके आउटपुट स्ट्रीम का आकार 5KB से 198KB तक है
  • समयपूर्व विंडो के भीतर समय-समय पर और प्रक्रिया से बाहर निकलता है


अपडेटेड कोड

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

संपादित करें:

MSBuild के साथ खेलने के घंटों के बाद मैं अंततः अपने सिस्टम में समस्या को पुन: पेश करने में सक्षम था


अनुभाग बी: समस्या मनोरंजन और समाधान

MSBuild में-m[:number]स्विच होता है जिसका उपयोग भवन बनाते समय उपयोग करने के लिए अधिकतम समवर्ती प्रक्रियाओं को निर्दिष्ट करने के लिए किया जाता है।

जब यह सक्षम हो जाता है, तो MSBuild कई नोड्स बनाता है जो बिल्ड पूर्ण होने के बाद भी चालू रहता है। अब, Process.WaitForExit(milliseconds)इंतजार कभी बाहर नहीं होगा और अंततः समय समाप्त होगा

मैं इसे कुछ तरीकों से हल करने में सक्षम था

  • स्पॉन MSBuild प्रक्रिया अप्रत्यक्ष रूप से 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
  • MSBuild का उपयोग करना जारी रखें लेकिन नोड्यूस को गलत पर सेट करना सुनिश्चित करें

    $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
  • यहां तक ​​कि अगर समानांतर निर्माण सक्षम नहीं है, तो आप अभी भी सीएमडी केWaitForExit माध्यम से बिल्ड लॉन्च करके अपनी प्रक्रिया को लटकने से रोक सकते हैं और इसलिए आप बिल्ड प्रक्रिया पर सीधा निर्भरता नहीं बनाते हैं

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

2 के दृष्टिकोण को प्राथमिकता दी जाती है क्योंकि आप नहीं चाहते कि बहुत सारे MSBuild नोड्स के आसपास झूठ हो।


इसलिए, जैसा कि मैंने ऊपर कहा था, धन्यवाद, ऐसा "-nr:False","-m:3"लगता है कि MSBuild हैंग-ईश व्यवहार तय हो गया है, जो Rx solutionपूरी प्रक्रिया को कुछ हद तक विश्वसनीय (समय दिखाने वाला) बनाया है। काश, मैं दोनों जवाब स्वीकार कर सकता या दो इनाम दे सकता
— जोएल्टी

@ जॉयल्टी मैं सिर्फ यह जानने की कोशिश कर रहा था कि क्या Rxअन्य समाधान में दृष्टिकोण बिना आवेदन के समस्या को हल करने में सक्षम है -nr:False" ,"-m:3"। मेरी समझ में यह गतिरोधों और अन्य सामानों से अनिश्चितकालीन प्रतीक्षा को संभालता है जिन्हें मैंने खंड 1 में कवर किया है। और धारा 2 में रूटकॉज है जो मुझे विश्वास है कि आपके द्वारा सामना की गई समस्या का मूल कारण है;) मैं गलत हो सकता हूं, यही कारण है कि मैंने पूछा, केवल समय बताएगा ... चीयर्स !!
— क्लिंट

3

समस्या यह है कि यदि आप StandardOutput और / या StandardError को पुनर्निर्देशित करते हैं तो आंतरिक बफर पूर्ण हो सकता है।

उपरोक्त मुद्दों को हल करने के लिए आप प्रक्रिया को अलग-अलग थ्रेड में चला सकते हैं। मैं WaitForExit का उपयोग नहीं करता हूं, मैं प्रक्रिया से बाहर निकलने वाली घटना का उपयोग करता हूं जो कि प्रक्रिया के ExitCode को अतुल्यकालिक रूप से यह सुनिश्चित करेगा कि इसे पूरा कर लिया है।

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

उपरोक्त कोड कमांड लाइन आर्ग्युमेंट्स के साथ FFMPEG.exe को बुलाकर लड़ाई का परीक्षण किया गया है। मैं एमपी 3 फ़ाइलों के लिए mp4 फ़ाइलों को परिवर्तित कर रहा था और बिना असफल हुए एक समय में 1000 से अधिक वीडियो कर रहा था। दुर्भाग्य से मेरे पास प्रत्यक्ष शैल अनुभव नहीं है, लेकिन आशा है कि इससे मदद मिलेगी।


यह कोड अजीब है, इसी तरह अन्य समाधान एफआईएसटी के प्रयास में विफल (अटक) गए हैं और फिर ठीक काम कर रहे हैं (अन्य 5 प्रयासों की तरह, मैं इसे और अधिक परीक्षण करूंगा)। Btw आप प्रदर्शन क्यों करते हैं BegingOutputReadlineऔर फिर प्रदर्शन ReadToEndAsyncकरते हैं StandardError?
— जोएल

ओपी पहले से ही अतुल्यकालिक रूप से पढ़ रहा है, इसलिए यह संभावना नहीं है कि कंसोल बफर पर एक गतिरोध यहां मुद्दा है।
— याकोव

0

निश्चित नहीं है कि यह आपका मुद्दा है, लेकिन MSDN को देखते हुए अतिभारित WaitForExit के साथ कुछ अजीब लगता है जब आप आउटपुट को अतुल्यकालिक रूप से पुनर्निर्देशित कर रहे हैं। MSDN आलेख, WaitForExit को कॉल करने की अनुशंसा करता है जो अतिभारित विधि को कॉल करने के बाद कोई तर्क नहीं लेता है।

डॉक्स पृष्ठ यहाँ स्थित है। प्रासंगिक पाठ:

जब मानक आउटपुट को अतुल्यकालिक घटना संचालकों पर पुनर्निर्देशित किया गया है, तो संभव है कि जब यह विधि वापस आती है तो आउटपुट प्रोसेसिंग पूरी नहीं हुई होगी। यह सुनिश्चित करने के लिए कि एसिंक्रोनस ईवेंट हैंडलिंग पूरी हो गई है, WaitForExit () अधिभार को कॉल करें जो इस अधिभार से एक सच्चे प्राप्त करने के बाद कोई पैरामीटर नहीं लेता है। यह सुनिश्चित करने में मदद करने के लिए कि Windows प्रपत्र अनुप्रयोगों में Exit ईवेंट को सही तरीके से हैंडल किया गया है, SynchronizingObject गुण सेट करें।

कोड संशोधन कुछ इस तरह दिख सकता है:

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

इस उत्तर केprocess.WaitForExit() लिए टिप्पणियों द्वारा संकेत के रूप में उपयोग के साथ कुछ पेचीदगियां हैं ।
— इन्जेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.