Force a main Thread element to run in another created thread - c#

I'm working on a C# winforms application.
I try to execute a code that return a huge data from database (Using LINQ with Entity Framework) on a new created Thread, and I show the data returned in DataGridView.
Code block :
GVentesEntities context=new GVentesEntities();
Thread t =new Thread(() =>
{
var R = context.Client.AsQueryable();
Invoke(new Action(() =>
{
dataGridView1.DataSource = R.ToList();
}));
});
t.Start();
The problem is after starting the Form the data load slowly in DataGridView, as I know that DataGridView runing on the main Thread.
How can I let DataGridView runing on another new Thread for fast loading data ? Or if you have any other solution I don't know about it.
Massive thanks in advance.

You're executing the query back on the UI thread. Don't do that. Call ToList() outside of the Invoke, and then you're only passing the results to the grid. Of course, there may still be performance issues with that, but that's a rather broad topic of dynamic data loading.
Multi-threaded GUI is very tricky, and rarely worth the trouble. Try to separate business logic from GUI instead, and make sure the GUI doesn't do too much unnecessary stuff. In general, if you want to show a lot of data in a component, you need to use e.g. a virtual grid - instead of building all the grid cells when you assign data to the DataSource, you only create them on demand (when they become visible etc.). Unlike, say, Delphi, this doesn't happen automatically with .NET's DataGridView - you need to write a bit of code.

You are right. If you have a WinForms application, and you need to do some lengthy calculations, it is a good idea to start a separate thread to do this. In the mean time your user interface thread is free to do other things, like react on buttons, update the display and show the progress of the lengthy calculations.
A fairly outdated method to do this, was to start the BackGroundWorker class. Although this method works fine, people nowadays tend to use async-await for this, because it is simpler to use.
Usually you see async methods when somewhere deep inside the procedure the thread must wait idly for another process to finish: fetch data from a database; write data to a hard disk; read information from the internet, etc. If you use async-await, then your thread won't wait idly, but goes up the call stack to see if one of the callers is not awaiting. This way your user interface will still be responsive, provided that the thread is not busy doing calculations.
The standard method of async-await in WinForms is something like this:
private async void Button1PressedAsync(object sender, ...)
{
Button button = (Button)sender;
button.Enabled = false;
this.ShowProgress();
// Start the Task, if you can do something else, don't await yet
Task<string> taskFetchData = this.FetchDataAsync();
DoSomethingElse();
// wehtn you need the result of the task: await for it
string fetchedData = await taskFetchData;
this.ShowFetchedData(fetchedData);
this.HideProgress();
button.Enabled = true;
}
private async Task<string> FetchDataAsync()
{
using (var textReader = System.IO.File.OpenText(this.FileName))
{
return await textReader.ReadToEndAsync();
}
}
What we see here:
Every function who wants to use async-await must be declared async
Return Task<TResult> instead of TResult, return Task instead of void. Only exception: event handlers return void instead of Task: no one will have to await an event handler. In Forms you usually see them as button clicks, mouse clickes, menu selections etc.
Start the async task, if you don't need the result immediately, don't await yet,
but do other useful stuff.
Only when you need the result of the task, or at last just before you return await the task, so you are certain that the task is finished when you are finished. Alternative: return the task and let your caller await it.
Because the thread that returns after the await has the same "context", this thread can be regarded as the UI thread: it can update user interface elements. No need for IsInvokeRequired etc.
Back to your question
You want to keep you UI responsive while the data is being fetched from the database. If the database management system can do most of the work, you are lucky, a simple async-await is enough.
It depends a bit on the method that you use to communicate with the database. You will have to search for async methods. I'll give an example using SQL:
private async Task<List<Order>> FetchOrderOfCustomer(int customerId)
{
const string sqlText = "SELECT Id, OrderDate, ... FROM Customers"
+ "WHERE CustomerId = #CustomerId";
List<Order> orders = new List<Order>();
using (var dbConnection = new SQLiteConnection(this.dbConnectionString))
{
using (var dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = sqlText;
dbCommand.Parameters.AddWithValue("#CustomerId", customerId);
dbConnection.Open();
using (SQLiteDataReader dbReader = await dbCommand.ExecuteReaderAsync())
{
while (await dbReader.ReadAsync())
{
var order = new Order
{
Id = dbReader.GetInt64(0),
...
};
orders.Add(forder);
}
}
}
}
return orders;
}
Most of the work is done by the Database management system. The only work that you do is copying the fetched data to Order and adding it to the List.
Although this can be optimized a little, if you use a method like this, your user interface will be quite responsive. For instance if you click a button to update the datagridview:
private async void ButtonUpdate_Clicked(object sender, ...)
{
this.buttonUpdate.Enabled = false;
this.ProgressBar.Value = this.ProgressBar.Minimum;
this.ProgressBar.Visible = true;
// Start the task to fetch the data, don't await for the result yet:
var taskFetchOrders = FetchOrdersOfCustomer(this.CustomerId);
// while the data is being fetched, which takes half a minute,
// show some progress.
while (!taskFetchOrders.Completed)
{
ProgressBar.PerformStep();
await Task.Delay(TimeSpan.FromSeconds(0.5));
}
List<Order> fetchedOrders = taskFetchOrders.Result;
this.UpdateDataGridView(fetchedOrders);
// clean up the mess:
this.progressBar.Visible = false;
this.buttonUpdate.Enabled = true;
}
Here you see that I don't await for the results of the task. I need to do something else: I need to update the ProgressBar. If your task is less than a few seconds, I wouldn't bother and just await taskFetchOrders.
Because I await Task.Delay(...), my user interface is still responsive: it can react on other buttons, resizes, show dialog boxes, update progress bars etc. Every half second the Delay task is completed. It checks whether taskFetchOrders is completed, and if not it updates the progress bar and Delays again.
Hope this has given you some insight in how to use async-await in order to keep your application responsive.

Related

Universal Windows UI Responsiveness with Async/Await

Please help me understand how to properly await long executing tasks to keep the UI responsive in a Universal Windows application.
In the code below OperateSystem is a model class which inherits ObservableObject. OperateSystem.GetLatestDataFromAllDevices connects to a variety of instruments, collects data, and updates class properties with the information from the instruments. The views update with values from Operate System.
The UI is not responsive while the dispatcher.RunAsync task is running, I added a Thread.Sleep(5000) to GetLatestDataFromAllDevices() to make sure and it locks up the UI for 5 seconds. Without the await Task.Delay(refreshTimer) the UI never updates (I'm assuming it instantly goes back into the GetLatestDataFromAllDevies before the UI can update). Setting the refreshTimer to 1ms allows the UI to update, but I know that's a workaround for another issue that needs to be fixed.
public ProductionViewModel()
{
OperateSystem = new OperateSystem();
StartButtonCommand = new RelayCommand(StartMeasurementSystem);
StopButtonCommand = new RelayCommand(StopMeasurementSystem);
if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
}
}
private async void StartMeasurementSystem()
{
stopRequest = false;
StopButtonEnabled = true;
StartButtonEnabled = false;
while (!stopRequest)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => OperateSystem.GetLatestDataFromAllDevices(ConfigurationSettingsInstance));
await Task.Delay(refreshTimer);
}
}
In OperateSystem
internal void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
GetDataInstrument1(configurationSettings);
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
Each of the GetDataInstrumnet methods connect to an instrument, gathers data, performs some scaling/formatting, and updates a class property with the current value.
I followed other S.O. answers to use the dispatcher.RunAsync as using other async methods I would get thread mashalling errors. But now I think the dispatcher is just marshaling these tasks on the UI thread anyway so it still blocks UI udpates.
To recreate the thread marshalling errors, I made GetLatestDataFromAllDevices async, and awaited a method executed as a task.
internal async void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
await Task.Run(()=>GetDataInstrument1(configurationSettings));
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
This results in:
System.Exception: 'The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))'
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
Since you have an unresponsive UI, you must push the work to a background thread (e.g., Task.Run).
For marshalling updates back to the UI thread, I recommend (in order of preference):
Using the return value of asynchronous methods. E.g., MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));.
Using Progress<T> to get multiple values from asynchronous methods. E.g., var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));.
Capturing a SynchronizationContext in your background classes and using that for sending updates to the UI thread. This is the least recommended because it results in your background driving your UI instead of the other way around.

await task.delay helps in UI refresh faster, but how?

I have a view model that is fetching a row of records and displaying on the Windows phone UI.. This view model method which fetches the data is doing a lot of Tasks, all marked with Await operations..
Looks like below:
async Task GetData()
{
var dataCollection = await GetSomeData();
await DoThis();
await DoThat();
}
The UI refreshes after the 'DoThis' call is invoked.
Now I just observed that if I introduce a Task.Delay in the code before other Tasks are done, the UI is refreshed immediately.. Which was my original Goal, to refresh the UI immediately after 'GetSomeData' Returns and then fire the other Tasks.
Looks like below:
async Task GetData()
{
var dataCollection = await GetSomeData();
await Task.Delay(100);
await DoThis();
await DoThat();
}
So what I understand from this, is that the UI thread gets opportunity to refresh after a call to Task.Delay is made. However without Task.Delay, if DoThis is called, some time is lost before it finds the first awaitable method in 'DoThis' so that it can return and continue with UI Refresh.
My questions are:
Have I understood it correct?
Is it safe to put this in production code?
Thanks in advance and hope I have explained clearly..
Pr
Below is Details of These methods as without those it is not clear what is going in the program.. :(
async Task<ObservableCollection<MyModel>> GetSomeData()
{
return await Task.Run(() =>
{
using (var db = new MainModelDataContext())
{
List<MyModel> temp =
db.Models.Where(Some condition)
.Take(30)
.ToList();
var returnCollection = new ObservableCollection<MyModel>(temp);
return returnCollection;
}
}
The ObservableCollection is bound to a list control on the UI Page. This method is being called by the page view model.
async Task DoThis()
{
// do some data processing and then post that to the Server
// this is the first awaitable method after the data processing
await (an HttpClientHandler).PostAsync();
}
Task DoThat() also follows the same flow as DoThis.. The data processing is also wrapped in async-await Tasks and they are just working on some class properties.
Hope I am clear.. Thanks again all
When you call Task.Delay, you return control to the UI message loop for those 100 milliseconds, and the UI message loop has the opportunity to process more messages from its queue. That's why you're experiencing the "refreshing" effect. If your GetSomeData method is truely asynchronous, your UI should remain responsive during its operation, and when it completes, it will continue to execute the next await.
If this effect doesn't happen, then that means your methods aren't really asynchronous, and its more likely that they are running a costly operation in a synchronous fashion which is blocking your UI thread.
Before putting this into production, you have to look into why you need Task.Delay to refresh your UI.

Update UI while Task.Run or TaskFactory.StartNew is still working

So I have decided to rewrite my Mail client in WPF as I think it's about time I move on from Windows Forms (I still like it), but I am facing a bit of a problem.
I use a BackgroundWorker in my Windows Forms app to do stuff and in a foreach I worker.ReportProgress(currentProgress); and this allows me to update the UI as things are being done in the background which is great.
But just now after starting a new WPF project, I notice that there is no BackgroundWorker in the Toolbox (for WPF apps) so I go searching online, and found that some people have problems updating the UI while using BackgroundWorker with WPF. So this makes me think that using BackgroundWorker in a WPF app is a bit hacky - and I don't want that.
On that same page, another user refers them to this page, telling them to use Task.Run instead of BackgroundWorker in WPF. Upon looking at Task.Run docs, I immediately see how it can be useful, however I do have one concern. I do not see a way to "Report Progress" or to update the UI as things are being done. All I see is how to Run a Task and "await" it; leaving me with just one option - update the UI after the long-running Task has completed.
How can we update the UI of a WPF desktop app while Task.Run/TaskFactory.StartNew is still working?
You can stick with BackroundWorker if you so choose. There is nothing really hacky about it although it is very old-school. As others said, if you can't find it in your toolbox, you can always declare and initialise it straight from your code (don't forget the using System.ComponentModel; directive).
Stephen Cleary has an excellent series of blog posts on BackgroundWorker vs Task, which highlights the differences and limitations of each approach. It's definitely worth a read if you're on the fence or just curious.
http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html
If you do decide to go down the Task + async/await route, there are a couple of things specifically related to progress reporting that you should keep in mind.
Generally you should be aiming to have your await Task.Run encapsulate the smallest meaningful amount of work possible. The rest of your async method will then execute on the dispatcher SynchronizationContext (assuming that it was started on the dispatcher thread) and will be able to update the UI directly, like so:
List<object> items = GetItemsToProcess();
int doneSoFar = 0;
foreach (var item in items)
{
await Task.Run(() => SomeCpuIntensiveWorkAsync(item));
doneSoFar++;
int progressPercentage = (int)((double)doneSoFar / items.Count * 100);
// Update the UI.
this.ProgressBar.Value = progressPercentage;
}
This is the easiest way of implementing progress reporting in the async world.
The only time I can imagine reporting the progress from within the body of the delegate you pass to Task.Run is when you're processing a very large number of items, and the processing of each item takes a very short time (we're talking 10,000 items per second as a rough guide). In such a scenario creating a large number of extremely fine-grained Tasks and awaiting them will introduce significant overhead. If this is your case you can fall back to the progress reporting mechanism introduced in .NET 4: Progress<T>/IProgress<T>. It's quite similar to the way the BackgroundWorker reports progress (in that it relies on events) and it provides a bit more flexibility in terms of deciding when you get to post back to the dispatcher context.
public async Task DoWorkAsync()
{
// Let's assume we're on the UI thread now.
// Dummy up some items to process.
List<object> items = GetItemsToProcess();
// Wire up progress reporting.
// Creating a new instance of Progress
// will capture the SynchronizationContext
// any any calls to IProgress.Report
// will be posted to that context.
Progress<int> progress = new Progress<int>();
progress.ProgressChanged += (sender, progressPercentage) =>
{
// This callback will run on the thread which
// created the Progress<int> instance.
// You can update your UI here.
this.ProgressBar.Value = progressPercentage;
};
await Task.Run(() => this.LongRunningCpuBoundOperation(items, progress));
}
private void LongRunningCpuBoundOperation(List<object> items, IProgress<int> progress)
{
int doneSoFar = 0;
int lastReportedProgress = -1;
foreach (var item in items)
{
// Process item.
Thread.Sleep(1);
// Calculate and report progress.
doneSoFar++;
var progressPercentage = (int)((double)doneSoFar / items.Count * 100);
// Only post back to the dispatcher SynchronizationContext
// if the progress percentage actually changed.
if (progressPercentage != lastReportedProgress)
{
// Note that progress is IProgress<int>,
// not Progress<int>. This is important
// because Progress<int> implements
// IProgress<int>.Report explicitly.
progress.Report(progressPercentage);
lastReportedProgress = progressPercentage;
}
}
}

C# Spawn new thread to access a web service to abort

I'm putting together a simple C# app where a user types commands into a "command bar" and the results are fetched in real time.
For example I could type, google stackoverflow, and it sends an API call off to google, fetches each of the results and displays them in the ui.
Currently I fire off the "search" method if a user pauses for more than 1/4 of a second on typing, so if you paused in the middle it could fire google stack and google stackoverflow.
Now in reality the api is doing a (rather slow) database query and this causes the ui to lock up while the first search completes, before it tries to start on the second search.
Is there a simple (C# 4.0) way to run the search call in a separate thread that I can then cancel/abort if the user continues typing?
e.g.
Task<string> thread;
string getSearchResults(string input) {
... Do some work ...
}
string userPaused(string search) {
if(this.thread.isRunning()) this.thread.Kill();
this.thread = new Task<String>(getSearchResults(string input);
return this.thread.result();
}
I've looked at the Tasks api and it doesn't look as if you can kill a task in the middle of work, it suggests using a while look and passing a shouldStop boolean, however during an API call to download the results there is no while loop.
The Threading documentation however points you to tasks if you need to get the return value.
What you may do with Tasks is to create them, and then cancel when not needed any more. If you can't cancel operation you are doing (like database query) - you can always cancel before results get returned. Your code may be something like this (not tested, just a draft):
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
var task = Task.Factory.StartNew(() =>
{
ct.ThrowIfCancellationRequested();
var result = Database.GetResult(); // whatever database query method you use.
ct.ThrowIfCancellationRequested();
return result;
}, tokenSource2.Token);
So as you can see it will query database and return value when no cancellation requested, but if you will try to cancell the task - it will not return value but rather throw OperationCanceledException you need to catch. For details visit MSDN Task Cancellation, but I think this should give you an idea. Don't worry about big task number - if your query is not very slow it won't matter - user will not be able to trigger so many searches. If you have asynchronous way of querying database - you can improve this code a bit more, but that also shouldn't be too hard.

Window of Main UI freezes although calculations run in differents tasks

I have a question about solving UI freezing.
Introduction:
I am currently programming an OPC based online alarm reader for a given analyzer tool. This tool received data from an excel sheet, analyzed this data using a rule base and a topology model and shows the results with TreeViewItems.
My task is to replace the excel sheet reader by a real time alarm reader.
That's done, I can connect my software to server and receive data packages every time new alarms are created.
Problem:
My solution for transport the new data to the main class and from there to the analyzer class is saving the data in a list, adding them to an EventArgs and raising an Event. The handling method in the main class receives this data, starts a new task (Task>) for the analyzer and returns its results to the Main Thread.
This construction should have uncoupled the calculation process from the UI The analyzing process takes about 1.3s with example data. New data arrives every 2 seconds on average. 2 seconds is in the same time the highest refreshing time.
Attached is a code snippet from the handling method
Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
()=>
{
/*if the alarmlist is emty, set the new alarms as alarmlist,
* else add the new alarms to the present list*/
if (AlarmList1.Count == 0)
AlarmList1 = e.CurrentAlarms;
else listModifier.mergeList(e.CurrentAlarms, AlarmList1);
/*Start the analysis process in a seperate task and return the Alarm_Group-List*/
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
Unfortunately the UI still is stuck when I start analyzing process and I don't even know if my concept of starting a new thread every two seconds(average appearance time of new alarms) is a reasonable concept.
I guess the problem is inside showAlarmLog but it is a lot of code. If needed I will post this code, too.
I would be thankful for any suggestions on this problem, even a "your concept is crap, try this idea: ..." would be good to know.
Kind regards
Larimow
The problem is that this call:
List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;
Blocks the UI thread until the background task is completed.
The way to handle this is to use a continuation task instead of waiting on the result:
Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
()=> {
// Keep existing code
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
// Make a continuation here...
analysertask.ContinueWith( t =>
{
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = t.Result;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
}, TaskScheduler.FromCurrentSynchronizationContext());
By scheduling this as a continuation, it will run when the first task completes. By using TaskScheduler.FromCurrentSynchronizationContext, you say to marshal this back onto the UI thread when it executes.
Note that this becomes far easier with .NET 4.5/C# 5. With C# 5, you could write this as:
var analysertask = Task.Run(
()=> {
// Keep existing code
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = await analysertask;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
This requires the method to be, itself, flagged with the new async keyword, however.

Categories

Resources