Luka Horvat
Luka Horvat

Reputation: 4402

External process blocks main thread when asynchronously reading output

So I have the following code in my WPF application

        ghci = new Process
        {
            StartInfo = new ProcessStartInfo("ghci")
            {
                WorkingDirectory = System.IO.Path.GetDirectoryName(path),
                Arguments = System.IO.Path.GetFileName(path),
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            }
        };
        ghci.OutputDataReceived += (obj, data) =>
        {
            this.Dispatcher.InvokeAsync(() =>
            {
                ghciOutput.AppendText(data.Data + "\n");
                ghciOutput.Focus();
                ghciOutput.CaretIndex = ghciOutput.Text.Length;
                ghciOutput.ScrollToEnd();
                ghciInput.Focus();
                Keyboard.Focus(ghciInput);
            });
        };
        ghci.Start();
        ghci.BeginOutputReadLine();

But when the process starts outputting large amounts of data the event fires very fast for every line in that output and the whole application freezes. Why does that happen? I thought I have made sure it didn't by firstly doing BeginOutputReadLine which is asynchronous (though it's quite possible I don't know what that means exactly) and then doing InvokeAsync inside the event to make sure the main thread updates the output when it has time.

I should also note that the output textbox doesn't update with new data when the application freezes, though that might just be a consequence of the app freezing.

Upvotes: 2

Views: 109

Answers (1)

Marcel N.
Marcel N.

Reputation: 13976

You need to buffer the incoming data in tje OutputDataReceived buffer and dispatch it via InvokeAsync to the UI in chunks (every 2-3 seconds) or so, in any case more infrequently than you do now.

You can buffer into a string and you can dispatch every 100 lines or so (in order to not measure time). Don't forget to clear the string after dispatching or just use another instance and let the old one be collected.

At the moment you're killing the UI thread with UI updates.

I think this is pretty clear. If you need an example, let me know and I'll see what I can do.

Upvotes: 2

Related Questions