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.
Related
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.
I want to run a task which queries a database for new messages for a user.
I want the task to run every x seconds, in a new tread so it doesn't cause the UI to become unresponsive.
If the database task finds messages then i would want it to make these messages available to the UI.
I thought the whole loop would run in its own thread, rather than have a loop in the UI thread that keeps creating a new thread every x seconds. I thought that would stop multiple calls to the database e.g. if i set the lookup to every 5 seconds and the database took longer than 5 seconds to respond.
I've been looking for a good few hours - the best article i found was:
https://blogs.msdn.microsoft.com/benwilli/2016/06/30/asynchronous-infinite-loops-instead-of-timers/
I'm new to threading and the above link seems relatively simple in its last example DoWorkAsyncInfiniteLoop, however it seems to run on the UI thread (although it mentions you could then use Task.Run to make it run in its own thread, and i'm not sure how found messages would be available to the UI thread.
Any advice would be greatly appreciated!
Using SQLDependency you will no more need to add an infinite loop. see below link:
SQLDependency Using C#
by using a new thread in WindowsForm you cant directly access to UI Elements because they are in your main thread. in this Situation you must use Dispatchers, here is an explanation about it:
Access UI Element From Another Thread
OK had some minor difficulty - can't use a dispatcher as i am using MVVM and the view model doesn't have a dispatcher as it is not a derived from a UI base. Here is my final code for anyone else trying to achieve this
public MainViewModel() //the constructor for the viewmodel
{
_Repo = CurrentApp.MainController.RepositoryManager; // this is my database access methods
Task t = Task.Run(CheckMessagesAsyncInfiniteLoop); //run the new thread
}
private async Task CheckMessagesAsyncInfiniteLoop() //this is my infinite loop as described in the above link, but run from the above Task.Run
{
while (true)
{
// Check the messages in the database
Messages = _Repo.Service_Message_GetList(CurrentApp.CurrentUser.BasicInfo.UserID);
// pause for the next check
await Task.Delay(30000);
}
}
Repository.DomainLayer.MessageCollection _Messages; //the collection that will be updated by the thread above
public Repository.DomainLayer.MessageCollection Messages //the property that my view is bound to
{
get
{
return _Messages;
}
set
{
_Messages = value;
NotifyPropertyChanged();
}
}
I developed a small client (WPF) to make some stress test on our systems. Essentially it has to call various methods on an Asp.Net WebApi endpoint in parallel.
Each time you press "Start" it generates 4000 tasks (Async - Await) in parallel with request to stress, waits until they all finish, then it does it again - until the user clicks the stop button. The GUI is decorated with a progress bar and some counters: requests in error, completed request, in progress requests. I obtain these informations because the object that makes the batch of stress requests exposes some events:
var stressTestTask = new stressTestTask(LogService, configuration);
stressTestTask.ErrorRequestCountChanged += stressTestTask_ErrorRequestCountChanged;
stressTestTask.GoodRequestCountChanged += stressTestTask_GoodRequestCountChanged;
stressTestTask.TryRequestCountChanged += stressTestTask_TryRequestCountChanged;
_executionCancellationToken = new CancellationTokenSource();
await Task.Run(
() => stressTestTask.ApiStressTestTask(_executionCancellationToken.Token),
_executionCancellationToken.Token);
The whole execution is started from an ICommand (MVVM):
private RelayCommand _startCommand;
public RelayCommand StartCommand
{
get
{
return _startCommand ?? (_startCommand = new RelayCommand(
async () =>
{
await StartStressTest();
}));
}
}
RelayCommand is an implementation of ICommand from the library Mvvm-Light.
What I don't understand is this behaviour: if I configure my batch of tasks with a "low" number of tasks, for example 2000, the GUI doesn't freeze while executing. If instead I choose 5000 tasks, after a while it freezes. If then I open another instance of the .exe of my client and I choose 2000 on each, the GUI is responsive in both.
My first question is: why opening one instance with x tasks is worse in terms of responsivness than opening n instances with x/n tasks? Is it something related to Windows Scheduler and the fact that in the first case I have only one process?
My second questions is: how can I address the problem to make everything work on a single GUI? I thought about making a console application with the single batch of stress tests and calling a command from the GUI for each instance I want, in order to generate a process for every batch.
Are you handling those API events by invoking to the UI context? If you have many invocations occurring you will flood the dispatcher with operations and cause the UI to hang and lag behind user input.
Try batching the UI updates.
My first question is: why opening one instance with x tasks is worse in terms of responsivness than opening n instances with x/n tasks?
Possibly because you are getting more events to handle on the UI thread. I guess your ErrorBetCountChanged, GoodRequestCountChanged and TryRequestCountChanged event handlers are invoked on the UI thread and a lot of events being raised may flood the UI thread.
As Gusdor suggets you should probably find a way of batching the updates. Take a look at the reactive extensions (Rx): http://www.introtorx.com/content/v1.0.10621.0/01_WhyRx.html.
It has a Buffer method that may come in handy: http://www.introtorx.com/content/v1.0.10621.0/13_TimeShiftedSequences.html.
It also has en Obervable.FromEvent method that you can use to convert an event into an IObservable: https://msdn.microsoft.com/en-us/library/hh229241(v=vs.103).aspx.
My second questions is: how can I address the problem to make everything work on a single GUI?
You need to find a way - one or anoher - of updating the UI less frequently. Batching the updates and events should be a good starting point. Raising less notifications is another option. Maybe you need to both.
how can I address the problem to make everything work on a single GUI?
Send API requests in "proper" async-await manner with only one thread.
private async Task SendStressRequests()
{
var tasks = new List<Task>();
for (int i = 0; i < 4000; i++)
{
var task = SendApiRequestAsync();
tasks.Add(task);
}
await Task.WhenAll(tasks);
// Update UI with results
}
I am inside a threat updating a graph and I go into a routine that makes a measurement for 4 seconds. The routine returns a double. What I am noticing is that my graph stops showing activity for 4 seconds until I am done collecting data. I need to start a new thread and put the GetTXPower() activity in the background. So in other words I want GetTXPower() and the graph charting to run in parallel. Any suggestions?
here is my code:
stopwatch.Start();
// Get Tx Power reading and save the Data
_specAn_y = GetTXPower();
_pa_Value = paData.Start;
DataPoint.Measurement = _specAn_y;
//Thread.Sleep(50);
double remaining = 0;
do
{
charting.stuff
}
uxChart.Update();
I suggest looking into Task Parallel Library.
Starting with the .NET Framework 4, the TPL is the preferred way to write multithreaded and parallel code.
Since you also need the result back from GetTXPower, I would use a Task<double> for it.
Task<double> task = Task.Factory.StartNew<double>(GetTXPower);
Depending on when you need the result, you can query if the task has completed by checking task.IsCompleted or alternatively block the thread and wait for the task to finish by calling task.Wait(). You can fetch the result through task.Result property.
An alternative would be to add a continuation to the initial task:
Task.Factory.StartNew<double>(GetTXPower).ContinueWith(task =>
{
// Do something with the task result.
});
Make a void method (I'll call it MeasureMentMethod) that collects the data. The create a Thread using the following code:
Thread MeasurementThread = new Thread(new ThreadStart(MeasurementMethod));
You can then run the thread with
MeasurementThread.Start();
And if your Thread has something like this:
while(true){
//Run your code here
Thread.Sleep(100);
}
Then you can just start it at the beginning, and it will just keep collecting data.
So, you would have your main thread that would update the chart, and you would start the thread that would get the data on the side.
Without using extra threads I would simply like to display a "Loading" label or something similar to the user when a large amount of data is being read or written. If I however attempt to modify any UI elements before calling the IO method, the application freezes for a while and then displays the "Loading" message after all the work is already done. This obviously doesn't help. How can I ensure that any UI changes are applied and visible before calling the IO method?
DataSet ds = STT_Import.ImportExcelToDataSet(filePath);
bool result = false;
if (ds != null)
{
int cellCount = ds.GetTotalCellCount();
if (Popup.ShowMessage(string.Format("Your file contains {0} cells. Inserting data will take approximately {1} seconds. Do you want to continue?",
cellCount, CalculateTime(cellCount)), "Confirm", MessageType.Confirm) == MessageBoxResult.Yes)
{
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
result = DB.StoreItems(_currentType, ds);
}
}
I tried looking for answers but couldn't find anything that answered my specific question, so I'm sorry if the question has been asked before.
When working with WPF, you can use the Dispatcher to queue commands on the UI thread at different DispatcherPriorities
This will allow you to queue your long-running process on the UI thread after everything in the DispatcherPriority.Render or DispatcherPriority.Loaded queues have occurred.
For example, your code may look like this:
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate() {
var result = DB.StoreItems(_currentType, ds); // Do Work
if (result)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
}));
It should be noted though that its usually considered bad design to lock up an application while something is running.
A better solution would be to run the long-running process on a background thread, and simply disable your application form while it runs. There are many ways of doing this, but my personal preference is using the Task Parallel Library for it's simplicity.
As an example, your code to use a background thread would look something like this:
using System.Threading.Tasks;
...
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
MyWindow.IsEnabled = False;
// Do actual work after user has been notified:
Task.Factory.StartNew(() => DB.StoreItems(_currentType, ds))
// This runs after background thread is finished executing
.ContinueWith((e) =>
{
var isSuccessful = e.Result;
if (isSuccessful)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
MyWindow.Enabled = true;
});
You are trying to solve the problem in the wrong manner. What you should be doing here is run the time-consuming task in a worker thread; this way, your UI will remain responsive and the current question will become moot.
There are several ways you can offload the task to a worker thread; among the most convenient are using the thread pool and asynchronous programming.
It is provably impossible to keep your UI responsive without utilizing additional threads unless your database provides an asynchronous version of the method you're using. If it does provide an asynchronous version of the method then you simply need to use that. (Keep in mind that async does not mean that it's using any other threads. It's entirely possible to create an asynchronous method that never uses additional threads, and that's exactly what's done with most network IO methods.) The specifics of how to go about doing that will depends on the type of DB framework you're using, and how you're using it.
If your DB framework does not provide async methods then the only way to keep the UI responsive is to perform the long running operation(s) in a non-UI thread.
The Approach you are using is not efficient way so I would suggest to go with Async Programing or threading
Async programming:
Visual Studio 2012 introduces a simplified approach, async programming, that leverages asynchronous support in the .NET Framework 4.5 and the Windows Runtime. The compiler does the difficult work that the developer used to do, and your application retains a logical structure that resembles synchronous code. As a result, you get all the advantages of asynchronous programming with a fraction of the effort. Support .Net framework 4.5
It will save your time to implementing System .Threading and very efficient for the task same as your where we have to wait for some operation
http://msdn.microsoft.com/en-ca/library/vstudio/hh191443.aspx
http://go.microsoft.com/fwlink/?LinkID=261549
or
Threading:
The advantage of threading is the ability to create applications that use more than one thread of execution. For example, a process can have a user interface thread that manages interactions with the user and worker threads that perform other tasks while the user interface thread waits for user input.Support .Net fremework 4.0 or Older
http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx
If you don't want the UI to be responsive I use a busy indicator.
There are prettier cursors - this is an in house application.
using (new WaitCursor())
{
// very long task
Search.ExecuteSearch(enumSrchType.NextPage);
}
public class WaitCursor : IDisposable
{
private Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
#region IDisposable Members
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
#endregion
}