UI is frozen while running Task - c#

May be I am wrong but my assuption is that any background thread can read and write into List or ObservableCollection if I don't care about any particular order. If I need a surtain order I will use BlockingCollection.
private void buttonTesting_Click(object sender, RoutedEventArgs e)
{
PrepareDataForTesting();
Stopwatch timer1 = new Stopwatch();
timer1.Start();
//some code preparing data
List<Task> tasks = new List<Task>();
//Testing for each pair
foreach (InterfaceWithClassName aCompound in Group1)
{
foreach (InterfaceWithClassName bCompound in Group2)
{
InstancePair pair = new InstancePair();
//some code
Task task = Task.Factory.StartNew(() => TestPairSerial(pair));
tasks.Add(task);
}
}
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
antecedents =>
{
timer1.Stop();
TimeSpan ts1 = timer1.Elapsed;
string elapsedTime1 = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts1.Hours, ts1.Minutes, ts1.Seconds, ts1.Milliseconds / 10);
statusLabel_1.Content = "Elapsed time to run the test" + elapsedTime1;
statusLabel_0.Content = "Testing made " + passes + " passes";
pairsResultsDataGrid.ItemsSource = pairsResultsTable.DefaultView;
System.Media.SystemSounds.Exclamation.Play();
}, CancellationToken.None, TaskContinuationOptions.None, ui);
System.Media.SystemSounds.Beep.Play();
}
(Note: I am not sure if it matters - "pair" is found through Reflection)
When I click the button I can hear the last line - System.Media.SystemSounds.Beep.Play(); meaning we out of the event handler and all the threads are launched. But then my application is still frozen untill ContinueWhenAll is done.
TestPairSerial(pair) method has the following structure:
private void TestPairSerial(object instances)
{
do
{
do
{
//here are two methods that read data from minData ObservableCollection
//minData is a public static property of MainWindow
//minData is bound to Chart control (in XAML)
} while (isSetbCompoundParams);
} while (isSetaCompoundParams);
//filling up results into one table and two dictionaries (main window variables)
}

You are executing the tasks in the main thread. You can execute the whole code asynchronously with Dispatcher.BeginInvoke
this.Dispatcher.BeginInvoke(new Action(() => {
// your code here
}), null);

Related

How can I stop the Task.Run()?

I'm newer to the concept of threading and I would like to use Task that is a component of Thread in my application because the save task takes time for executing.
This is my code:
private void SaveItem(object sender, RoutedEventArgs e)
{
// Button Save Click ( Save to the database )
Task.Run(() =>
{
var itemsS = Gridview.Items;
Dispatcher.Invoke(() =>
{
foreach (ItemsModel item in itemsS)
{
PleaseWaittxt.Visibility = Visibility.Visible;
bool testAdd = new Controller().AddItem(item);
if (testAdd)
Console.WriteLine("Add true to Items ");
else
{
MessageBox.Show("Add failed");
return;
}
}
PleaseWaittxt.Visibility = Visibility.Hidden;
});
});
MessageBox.Show("Save Done");
// update the gridView
var results = new Controller().GetAllItems();
Gridview.ItemsSource = null;
Gridview.ItemsSource = results;
Gridview.Items.Refresh();
}
The problem is that when I save all items, I got duplicate data in the database. Otherwise, the count of ItemsS is fixed to 300, but after the saving, I got 600,
Did Task.Run() repeat the save task to the database ?
NB: I'm working on UI project ( WPF Desktop app )
I'm thinking you'd need something along the lines of this.
I quickly whipped it up but i hope its enough to attempt a fix yourself.
private async void SaveItem(object sender, RoutedEventArgs e)
{
try {
var itemsS = GridviewServices.Items.ToList(); // to list makes shallow copy
await Task.Run(() => {
foreach (ItemsModel item in itemsS)
{
bool testAdd = new Controller().AddItem(item);
}
});
// Dont update ui in task.run, because only the ui thread may access UI items
// Do so here - after the await. (or use dispatcher.invoke).
GridviewServices.Items.Clear();
GridviewServices.Items = itemsS;
} catch { ... } // Handle exceptions, log them or something. Dont throw in async void!
}
I'm also thinking this would work:
private async void SaveItem(object sender, RoutedEventArgs e)
{
// Button Save Click ( Save to the database )
var itemsS = GridviewServices.Items;
await Task.Run(() =>
{
foreach (ItemsModel item in itemsS)
{
Dispatcher.Invoke(() => {PleaseWaittxt.Visibility = Visibility.Visible;})
bool testAdd = new Controller().AddItem(item);
if (testAdd)
Console.WriteLine("Add true to Items ");
else
{
MessageBox.Show("Add failed");
return;
}
}
Dispatcher.Invoke(() => {PleaseWaittxt.Visibility = Visibility.Hidden;})
});
MessageBox.Show("Save Done");
// update the gridView
var results = new Controller().GetAllItems();
Gridview.ItemsSource = null;
Gridview.ItemsSource = results;
Gridview.Items.Refresh();
}
The problem you're running in to, is because the Task you're executing isn't running in parallel, but synchronously to the rest of your application.
When you're running CPU-intensive tasks in the background of your UI-application, you'll want to either work with actual threads or async/await - which is what you attempted with your code.
What you'll want to do is something similar to this:
private async void SaveItem(object sender, RoutedEventArgs e) => await Task.Run(
/*optionally make this async too*/() => {
// Execute your CPU-intensive task here
Dispatcher.Invoke(() => {
// Handle your UI updates here
});
});
This is just a general overview, I don't know your exact use-case, but this should get you started in the right direction.
One thing to be weary of when using Lambdas and such, is closures.
If your application tends to use a lot of memory, you might want to re-think the structure of your calltree and minimize closures in your running application.

Adding UserControls to Grid Asynchronously [duplicate]

This question already has answers here:
Dispatcher and async await in WPF
(2 answers)
Closed 1 year ago.
I am working with a WPF application and I am facing problems while applying Navigation, the screen freezes , So I want to achieve Asynchronicity
My method of navigation : I create a grid and add User controls to the children property of that grid
and since I have so many UI elements on numerous different User controls it freezes the Application
I want to add A user control asynchronous on when the window is loaded, My idea is to use the async await keywords but obviously I am using them incorrectly, I have researched and do not understand why it is suggested to use dispatcher even after there being async await so I wanted to follow that way (async/await)
This is just a sample problem of the real deal
this is the code
private async void grid1_Loaded(object sender, RoutedEventArgs e)
{
txtb1.Text = "";
var watch = System.Diagnostics.Stopwatch.StartNew();
await gy();
watch.Stop();
var elapsedtm = watch.ElapsedMilliseconds;
txtb1.Text += $"TOTAL TIME {elapsedtm} \n\n\n";
}
private async Task gy()
{
////////////
Y1 child1 = new Y1();
await Task.Run(() => grid1.Children.Add(child1));
///////////
}
private async void grid1_Loaded(object sender, RoutedEventArgs e)
{
txtb1.Text = "";
var watch = System.Diagnostics.Stopwatch.StartNew();
//Asynchronous execution of the "gy" method in the UI thread.
await Dispatcher.InvokeAsync(gy);
watch.Stop();
var elapsedtm = watch.ElapsedMilliseconds;
txtb1.Text += $"TOTAL TIME {elapsedtm} \n\n\n";
}
// This method can only be called on the main UI thread.
private void gy()
{
////////////
UIElement child1 = new Y1();
grid1.Children.Add(child1);
///////////
}
If in the gy method there are some long-term operations without using UI elements and you need to free the main UI thread from their execution, then this option:
private async void grid1_Loaded(object sender, RoutedEventArgs e)
{
txtb1.Text = "";
var watch = System.Diagnostics.Stopwatch.StartNew();
await gy();
watch.Stop();
var elapsedtm = watch.ElapsedMilliseconds;
txtb1.Text += $"TOTAL TIME {elapsedtm} \n\n\n";
}
private async Task gy()
{
// Here's some lengthy code, but in which there are no calls to UI elements.
////////////
await grid1.Dispatcher.BeginInvoke(new Action(() =>
{
UIElement child1 = new Label() { Content = "Hello" };
grid1.Children.Add(child1);
}));
///////////
// Here's some lengthy code, but in which there are no calls to UI elements.
}

How to run multiple tasks in parallel and update UI thread with results?

I have a method which consists of two lists (1. items to search and 2. workers to search with). Each worker takes an item from the list, searches for it, and add the results to a global results list which update the UI thread (a listview).
This is what I came up with so far:
List<Result> allResults = new List<Result>();
var search = new Search(workers);
//Will be full with items to search for
var items= new ConcurrentBag<item>();
while (items.Any())
{
foreach (var worker in workers)
{
if (!items.Any())
break;
IEnumerable<Result> results = null;
Task.Factory.StartNew(() =>
{
if (ct.IsCancellationRequested)
return;
items.TryTake(out Item item);
if (item == null)
return;
results= search.DoWork(worker, item);
}, ct);
if (results?.Any() ?? false)
{
allResults.AddRange(reults);
}
//Update UI thread here?
}
}
The workers should search in parallel and their results added to the global results list. This list will then refresh the UI.
Am I on the right track with the above approach? Will the workers run in parallel? Should I update the UI thread within the task and use BeginInvoke?
This will run parallel searches from the list items up to a specified number of workers without blocking the UI thread and then put the results into a list view.
private CancellationTokenSource _cts;
private async void btnSearch_Click(object sender, EventArgs e)
{
btnSearch.Enabled = false;
lvSearchResults.Clear();
_cts = new CancellationTokenSource();
AddResults(await Task.Run(() => RunSearch(GetItems(), GetWorkerCount(), _cts.Token)));
btnSearch.Enabled = true;
}
private void btnCancel_Click(object sender, EventArgs e)
{
_cts?.Cancel();
}
private List<Result> RunSearch(List<Item> items, int workerCount, CancellationToken ct)
{
ConcurrentBag<List<Result>> allResults = new ConcurrentBag<List<Result>>();
try
{
Parallel.ForEach(items, new ParallelOptions() { MaxDegreeOfParallelism = workerCount, CancellationToken = ct }, (item) =>
{
Search search = new Search(); // you could instanciate this elseware as long as it's thread safe...
List<Result> results = search.DoWork(item);
allResults.Add(results);
});
}
catch (OperationCanceledException)
{ }
return allResults.SelectMany(r => r).ToList();
}
private void AddResults(List<Result> results)
{
if (results.Count > 0)
lvSearchResults.Items.AddRange(results.Select(r => new ListViewItem(r.ToString())).ToArray());
}
If your are working with Windows form, you can refer to How do I update the GUI from another thread?
If you are working with WPF. You can find your UI Dispatcher and use the dispatcher to update UI. Usually, even you try to update UI in a loop, it may not update the UI immediately. If you want to force to update UI, you can use DoEvents() method. The DoEvents() method also works for WPF. But try to avoid using DoEvents().

How is this not causing a cross thread exception in silverlight?

I am aware that you need to use a Dispatcher to update items in the UI thread from a worker thread. To confirm my understanding, when you get the Dispatcher associated with current object is it always the UI dispatcher if my class inherits from the UserControl class? In which cases is it not the UI dispatcher?
Anyway, in the following code, I am creating a query and starting it asynchronously and when it completes, it sets the itemsource on one of my UI elements. I also am adding items to an observable collection that a UI element uses as its itemsource. When this is ran, it works fine and isn't fussing at me to use a dispatcher and update the UI that way. Why is that?
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
QueryTask queryTask = new QueryTask(URL);
queryTask.ExecuteCompleted += new EventHandler<QueryEventArgs>(queryTask_ExecuteCompleted);
queryTask.Failed += new EventHandler<TaskFailedEventArgs>(queryTask_Failed);
Query query = new Query();
query.Where = "Field <> 'XXX'";
query.OutFields.Add("*");
queryTask.ExecuteAsync(query);
BuildingsOrganizationList.ItemsSource = organizationList;
}
void queryTask_ExecuteCompleted(object sender, QueryEventArgs e)
{
FeatureSet featureSet = e.FeatureSet;
foreach (KeyValuePair<string, string> columns in featureSet.FieldAliases)
{
TypeGrid.Columns.Add(new DataGridTextColumn()
{
Header = columns.Key,
Binding = new System.Windows.Data.Binding("Attributes[" + columns.Key + "]"),
CanUserSort = true
});
}
TypeGrid.ItemsSource = featureSet.Features;
TypeBusyIndicator.IsBusy = false;
testing();
}
private void testing()
{
List<string> temp = new List<string>();
temp.Add("Item 1");
temp.Add("Item 2");
temp.Add("Item 3");
foreach (string org in temp)
{
organizationList.Add(org);
}
}
Because even though the processing is done asynchronously, you retrieve the result in your UI thread (an event is NOT thread), and update it from there.
If, however, you put the code inside queryTask_ExecuteCompleted in a Task:
Task.Factory.StartNew(() =>
{
//code of queryTask_ExecuteCompleted here
});
You will get your exception.
The ExecuteCompleted event happens on the same thread that calls ExecuteAsync.

Parallel Programming: Can't access UI Parallel?

I'm trying to create parallel execution of a function in wpf c# which also runs actions on the UI. But when running there is always an exception at methods on UI Controls: The calling thread cannot access this object because a different thread owns it. The exception is always called on the second instance of the loop being run, so it isn't possible to manipulate the UI in two parallel running instances?
Is it possible to acces the UI in parallel?
Code:
do
{
if (listBox_Copy.SelectedIndex < listBox_Copy.Items.Count - 1)
{
listBox_Copy.SelectedIndex = listBox_Copy.SelectedIndex + 1;
listBox_Copy.ScrollIntoView(listBox_Copy.SelectedItem);
}
listBox_Copy.Focus();
huidigitem = listBox_Copy.SelectedItem as ListBoxItem;
currentitemindex = listBox_Copy.SelectedIndex;
currentitem = listBox_Copy.ItemContainerGenerator.ContainerFromIndex(currentitemindex) as ListBoxItem;
itemgrid = FindVisualChild<Grid>(currentitem);
senderbutton = (Button)sender;
Button playcues = itemgrid.FindName("Playbutton") as Button;
cuetrigger = itemgrid.FindName("cuetrigger") as TextBlock;
Jobs.Add(playcues);
} while (cuetrigger.Text != "go");
Parallel.ForEach(Jobs, playcues => { play(playcues, new RoutedEventArgs()); });
And then it crashes at the play function
private void play(object sender, System.Windows.RoutedEventArgs e)
{
Grid itemgrid = VisualTreeHelperExtensions.FindAncestor<Grid>(playcue);
...
}
It is not possible to access the UI from a background thread, all your updates must be on the main thread. You can do this by using the Dispatcher
Something like this
Action x = (Action)delegate {
//do my UI updating
};
Dispatcher.Invoke(x, new object[] { });
The trick is to use an IProgress<T> to report updates to the main thread. The IProgress<T> constructor accepts an anonymous function that will be run in the main thread and can thus update the UI.
Quoting from https://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html :
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}
Now, a little bit of self-promotion :) I created an IEnumerable<T> extension to run a parallel for with event callbacks that can directly modify the UI. You can have a look at it here:
https://github.com/jotaelesalinas/csharp-forallp
Hope it helps!

Categories