C#, WPF interface help with background workers

Hello everyone,

I’ve been wrestling with a Rhino development problem that involves WPF and background processes and decided to post this due to the reply generated by a previous post (thank you, Tom).

Enclosed is a ‘shell’ project that contains most of the processes used by the mentioned problem. Please note that I decided to keep the application as a stand-alone rather than attach it to a rhino process but I can do that work if needed.

My problem is this:

I have built a multithreaded plugin that uses various background processes (for example computational processes for a rhino model), two of which get chained together through a WPF interface.

ie.
WPF interface button click lets user start process
The model behind receives click event and triggers the background worker chain to start.
Chain completes tasks and then updates interface.

The problem I have been having is that some events, for example updating a WPF slider cause the application to crash. This version of the project is not crashing. I’m still trying to work out exactly why that is but decided to post this anyway.

Tom if you read this you mentioned the use of notify. I’ve not ever used that process before. I suspect it’s a very simple change to make but hope you, or someone else can take a look at this project and provide a hand / some advice or help. Not sure if this is just a notify problem or a variable binding problem with the Slider.maximum property.

Many thanks in advance.

WpfApp11.zip (96.8 KB)
(Edited to more closely mimic the process)

I couldn’t reproduce the bug just as you said. But what immediately catches my eye is the missing thread safety. If it just occurs infrequently, it may be due to runnning into a racing condition.

Being within another thread, accessing the Main Gui-Thread, you can do the following to ensure Thread safety:

// invoke within the GUI thread.
Dispatcher.CurrentDispatcher.Invoke(() =>
{
            // Whatever is meant to do after the work such as
           MainWindow.new_maximum = RandomNumber(0,100);
});

Another issue:

Events are being registered like this:

backgroundProcessTwo.DoWork -= ProcessWorker_DoWork;
backgroundProcessTwo.DoWork += ProcessWorker_DoWork;

backgroundProcessTwo.RunWorkerCompleted -= ProcessWorker_Completed;
backgroundProcessTwo.RunWorkerCompleted += ProcessWorker_Completed;

If you do your way (with anonymous methods), you might create more than one subscription.

You can actually simplify a lot:

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


namespace WpfApp11
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private Random _random = new Random(Seed: 42);

        public MainWindow()
        {            
            InitializeComponent();
        }
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RandomizeSliderMaximum();
        }

        private void UpdateSliderMaximum()
        {            
            double newMax = _random.Next(1, 100);

            if (MySlider.Maximum != newMax)
            {
                MySlider.Maximum = newMax;
                MySlider.Value = _random.Next((int)MySlider.Minimum,(int)MySlider.Maximum);
            }        
        }      

        private void RandomizeSliderMaximum()
        {
            Task.Run(() =>
            {
                Thread.Sleep(2000); // Simulate Work 
                Dispatcher.Invoke(() => UpdateSliderMaximum());                
            });
        }
    }
}

Tom,

It’s true, you’re right. When I wrote the application this version is not crashing (as stated in the original post). I am currently examining/learning about that issue along with the other issues you raised and I will reply to them shortly.

The issue that I am having with this version is that the slider maximum value is not changing upon completion of the background work. I understand the notification concept you had mentioned but am having trouble implementing it.

Could you please provide some help?

Tom,
Again thank you for taking the time to offer help with this, it’s really appreciated. This post is in relation to the “another issue” and if it’s okay with you I’d like to ask a question - Q - Why will I create more than one subscription?

Some sub-questions: Is it because I am de-subscribing then resubscribing? And in a related question does my de- and re-subscribe also cause problems with the “busy” check (shown below)?

if (!backgroundProcessTwo.IsBusy & path_status) { backgroundProcessTwo.RunWorkerAsync(); }

Tom,

Just a brief update. So thank you very much for making me learn a little more about thread safety. As a self taught programmer help like this is instrumental - thank you for being a member of the community!

I have wrapped the slider maximum change in this.Dispatcher.Invoke(() => { //process }); and it worked beautifully so yes the crash was a thread conflict. Your help is much appreciated.