简体   繁体   中英

Run Tasks on a separate thread with return value

I'm developping an application that should run a new task , makes some stuff and return a list of objects...

when spawning the new task , i need to simulate in my UI thread a progressbar changing in a for loop... and after the task completion , i have to update my view ( by updating the values of my ViewModel properties as i'm using MVVM pattern).

the problem is that when the task is launched, the GUI freezes and the progress bar is not updated! i thought that tasks are run in background on a sperate thread ( as they are pooled). so why my GUI is blocked??

here is a snippet of my code :

Task<List<Items>> myTask = Task.Factory.StartNew(() => queryHandler.GetItemssStatistics(items, sec, buyer, seller, From, To));

//Here is the simulation of ProgressBar changing that i need to be done when task is started
                    for (int i = 0; i < 100; i++)
                    {
Thread.Sleep(100);
                        StatusValue = i+1;
                    }
//Here is the section i need to do once the task finishes its execution to update my VM
//Wait for the task to finish its execution and retreive results:
                    List<Items> _tempResults = myTask.Result;
                    foreach (Items d in _tempResults)
                    {
                        ResultsList.Add(d);
                    }

                    if (ResultsList.Count == 0)
                    {
                        MessageBox.Show("Sorry, No items matched the defined research criteria!", "Warning",
                                        MessageBoxButton.OK,
                                        MessageBoxImage.Warning, MessageBoxResult.OK, MessageBoxOptions.ServiceNotification);
                    }
                    else
                    {
                        //Show items:
                        ResultsAvailable = Visibility.Visible;

                    }

There are a few problems with your code:

  • You're using Thread.Sleep on the UI thread
  • You're calling Task<T>.Result again on the UI thread - which blocks until the task has been completed.

You can do a few things:

  • Use ProgressBar.IsIndeterminate = true to show a marquee-style progress bar instead of faking the progress update or use a DispatcherTimer to update the progress bar.
  • Use task continuations to perform whatever code that needs to happen after the task completes. Alternatively, if you're using .NET 4.5, you can use the new async/await features of the language.

To use async/await:

async Task GetItems()
{
    List<Items> tempResults = await Task.Run(() => queryHandler.GetItemssStatistics(...));
    // the following will be executed on the UI thread automatically
    // after the task is done, keeping the UI responsive
    foreach (Items d in _tempResults)
    {
        ResultsList.Add(d);
    }
}

To use a continuation:

Task.Factory.StartNew(() => queryHandler.GetItemssStatistics(...))
    .ContinueWith(task => ProcessResults(task.Result), TaskScheduler.FromCurrentSynchronizationContext());

The FromCurrentSynchronizationContext method creates a task scheduler that uses SynchronizationContext to marshal the call back to the UI thread.

Clearly using await/async is much easier, and should be your preference if it's available.

You are still stopping the UI from working, because you are calling Thread.Sleep on it. If you want your progress-bar to update, run another thread, or try to integrate the increasing of the progress-bar in your task.
Looking at your code, it would be better to do the update of the progress-bar in your task!
You are making the user wait for 10000 ms, if your task finishes before, the user has to wait...
Access the dispatcher of the progress-bar in your task, when one items has been progressed and do like

//process item
if(progressBar.Dispatcher.CheckAccess()){
progressBar.Value += 1; //maybe calculate the number of items you are processing
}
else {
//send a delegate to the dispatcher
MyDelegate myDel = new MyDelegate(delegate() {
   progressBar.Value += 1;
});    
progressBar.Dispatcher.Invoke(myDel);
}

The delegate's signature could be something like:

public void MyDelegate();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM