multiple threads started by user. Using result only from last one

Viewed 83

I have application where user provides data to be handled by thread and then the thread returns the data into the UI. User can change the input starting another thread. How to properly ignore old returing threads ?

I noticed two solutions often desribed. 1) in the invoke check if current input data is same as the thread worked on 2) wait for first thread to end before starting another one

first solution works but still older thread may be used if user changed input again to same value and I get twice or more refreshing UI with the same data second option is less responsive especially when the calculations in thread run longer because we have to wait for each thread to complete its work

I created below solution and I wonder if it is proper way of handling it or I need to add some other check or lock.

private Thread lastThread;

private void ButtonClick(object sender, MouseButtonEventArgs e)
    {
    this.lastThread= new Thread(delegate () { Update(someData); });
    this.lastThread.Start();
    }

private void Update(int someData) {

    Thread currThread = Thread.CurrentThread;

    Application.Current.Dispatcher.Invoke(new Action(() => EndUpdate(
        someDataCalculated,
        currThread
        )));

private void EndUpdate(int someDataCalculated, Thread senderThread) {

        if (senderThread == this.lastThread)
            {
            // right thread, do some work in UI
           }

  }
2 Answers

if the user invoked few threads, and can update the data in any of them, I think that you should add a LastUpdated property. and keep track when the user made changes, and compare the threads by that.

You should use a tool that is specifically designed to handle this kind of thing - and that it Microsoft's Reactive Framework.

You can just write this code:

    public MainWindow()
    {
        InitializeComponent();

        IDisposable subscription =
            Observable
                .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                    h => Button.MouseLeftButtonDown += h,
                    h => Button.MouseLeftButtonDown -= h)
                .Select(x => Observable.Start(() => { Update(42); return 42; }))
                .Switch()
                .ObserveOnDispatcher()
                .Subscribe(x =>
                {
                    // latest result only, do some work in UI
                });
    }

This responds to the MouseLeftButtonDown event by invoking Update(42); return 42; on a background thread. It then calls .Switch() which is a special operator that ensure only the result of the latest call to Observable.Start(() => { Update(42); return 42; }) is returned (it ignores any previous ones regardless of if they come in first or not). It then marshalls back to the UI with the .ObserveOnDispatcher() operator. And Finally, the .Subscribe(x => allows the UI to respond to the returned value on the UI thread.

Please keep in mind that your example code in the question wasn't fully fleshed out so I fudged it a bit, but I think you get the idea.

Calling .Dispose() on the subscription detaches the event handling.

Just NuGet "System.Reactive.Windows.Threading" to get the bits and add using System.Reactive.Linq; to your code to get the extension methods you need.

Let me know if you need further explanation.

Related