Why does this puts GC pressure under WPF but not in a console application?

Viewed 46

I'm looking to realize a logging window that handles high-throughput under WPF.

There's something that I fundamentally don't understand, why does it put such a high pressure on the GC while the same thing run in a console does not at all.

Code:

enter image description here

using System;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1;

public partial class MainWindow
{
    private readonly StringBuilder _builder = new();
    private readonly object _syncRoot = new();

    public MainWindow()
    {
        InitializeComponent();

        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(BackgroundAction);
        Task.Factory.StartNew(ForegroundAction);
    }

    private async Task ForegroundAction()
    {
        var delay = TimeSpan.FromSeconds(1.0d / 60.0d);

        while (true)
        {
            await Task.Delay(delay);

            await Dispatcher.BeginInvoke(() =>
            {
                string text;

                lock (_syncRoot)
                {
                    text = _builder.ToString();
                }

                TextBox.Text = text;
            });
        }
    }

    private void BackgroundAction()
    {
        while (true)
        {
            lock (_syncRoot)
            {
                if (_builder.Length > 10000)
                {
                    _builder.Clear();
                }

                _builder.AppendLine(new string(DateTime.Now.ToString("O")));
            }
        }
    }
}

XAML:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    <Grid>
        <TextBox Name="TextBox" />
    </Grid>
</Window>

Console:

enter image description here

using System.Text;

internal class Program
{
    private static readonly StringBuilder _builder = new();
    private static readonly object _syncRoot = new();

    public static void Main(string[] args)
    {
        Task.Factory.StartNew(BackgroundAction);
        Task.Factory.StartNew(ForegroundAction);
        Console.ReadKey();
    }

    private static async Task ForegroundAction()
    {
        var delay = TimeSpan.FromSeconds(1.0d / 60.0d);

        while (true)
        {
            await Task.Delay(delay);

            string text;

            lock (_syncRoot)
            {
                text = _builder.ToString();
            }

            Console.WriteLine(text);
        }
    }

    private static void BackgroundAction()
    {
        while (true)
        {
            lock (_syncRoot)
            {
                if (_builder.Length > 10000)
                {
                    _builder.Clear();
                }

                _builder.AppendLine(new string(DateTime.Now.ToString("O")));
            }
        }
    }
}

The hot spot on both versions is _builder.AppendLine(new string(DateTime.Now.ToString("O")));.

The dispatcher isn't at fault in WPF, you can even set it to only be called 10 times per second instead of 60 as shown but the code itself will still inflict lots of pressure on the GC.

If this approach is fundamentally wrong, I'm ready to listen to alternatives.

0 Answers
Related