Search code examples
c#wpf.net-coreoptimizationtask

Multithreading bug in Release with code optimization on


I am opening a new subwindow from the main one on button click. Once loaded, the subwindow starts a task with mouse cursor position tracker. On subwindow closing, the signal variable stopMouseTracker is set to true and the infinite loop in the task is supposed to terminate. The code below works in both Debug and Release without code optimization. But when I run my code in Release with code optimization on, the subwindow freezes after I try to close it. What can cause the problem?

MainWindow:

public MainWindow()
{
    InitializeComponent();
}

private void button_Click(object sender, RoutedEventArgs e)
{
    SubWindow subWindow = new();

    subWindow.ShowDialog();
}

Subwindow:

bool mouseTrackerOn = false;
bool stopMouseTracker = false;

public SubWindow()
{
    InitializeComponent();

    StartMouseTracker();
}

private void StartMouseTracker()
{
    mouseTrackerOn = true;

    Task.Factory.StartNew(() =>
    {
        while (!stopMouseTracker)
        {
            var p = System.Windows.Forms.Cursor.Position;
            
            // Write cursor coordinates to text box tbXY
            tbXY.Dispatcher.BeginInvoke(() => tbXY.Text = $"{p.X} {p.Y}");

            Thread.Sleep(1);
        }

        mouseTrackerOn = false;
    });
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    stopMouseTracker = true;

    // This blocks the thread completely in Release with code optimization on
    while (mouseTrackerOn) ;
}

Solution

  • You have a non-locked variable which you are checking from a different thread. While volatile or Interlocked.Read etc would fix that, you didn't need all this messing about with tasks and the Dispatcher anyway.

    Just use a CancellationToken to abort the loop, and put the loop on the UI thread using async.

    private CancellationTokenSource _stopMouseTracker = new();
    
    public SubWindow()
    {
        InitializeComponent();
        StartMouseTracker(_stopMouseTracker.Token);
    }
    
    private async Task StartMouseTracker(CancellationToken cancellationToken)
    {
        try
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                var p = System.Windows.Forms.Cursor.Position;
                // Write cursor coordinates to text box tbXY
                tbXY.Text = $"{p.X} {p.Y}";
                await Task.Delay(1, cancellationToken);
            }
        }
        catch
        { //
        }
    }
    
    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        stopMouseTracker.Cancel();
    }