Assuming that a client app gets data from a server nearly in real time. What is the more efficient way to continuously update the UI based on the retrieved data. Think of multiple xaml controls, like texts that show numbers. Those get updated as long as the application is running. They never stop unless the user decides it. (let's say by pressing a stop button or exit the app)
Below I have a simple example utilizing async and await keywords. Is that a good way for my scenario? Or for example BackgroundWorker would be a better way?
private async void Button_Click_Begin_RT_Update(object sender, RoutedEventArgs e)
{
while(true)
textField1.Text = await DoWork();
}
Task<string> DoWork()
{
return Task.Run(() =>
{
return GetRandomNumberAsString();
});
}
*for the sake of simplicity I use code-behind and not mvvm in my example
Your code is more or less OK if your GetRandomNumberAsString() takes at least 15ms to complete.
If it takes less than that, and you want to minimize update latency i.e. you don't want to just wait, you might want to (1) replace your per-operation Task.Run with an endless loop that completely runs in a background thread (2) Implement throttling mechanism in that loop, and only update your GUI (using e.g. Dispatcher.BeginInvoke()) at around 30-60Hz.
P.S. The exact mechanism how you update your GUI (databinding + INotifyPropertyChanged, or directly like in your code) is not relevant for performance.
Update: here's example (untested)
static readonly TimeSpan updateFrequency = TimeSpan.FromMilliseconds( 20 );
void ThreadProc()
{
Stopwatch sw = Stopwatch.StartNew();
while( true )
{
string val = GetRandomNumberAsString();
if( sw.Elapsed < updateFrequency )
continue; // Too early to update
sw.Restart();
Application.Current.Dispatcher.BeginInvoke( () => { textField1.Text = val; } );
}
}
Related
i know the common ways of cancelling a backgroundworker using eventwaithandles...
but i wanna know is that right to use a while loop to trap and pause working of a backgroundworker ? i coded like this :
Bool stop = false;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100000;
progressBar1.Value = 0;
for (int i = 0; i < 100000; i++)
{
progressBar1.Value++;
if (i == 50000)
stop = true;
while (stop)
{ }
}
}
private void button1_Click(object sender, EventArgs e)
{
stop = !stop;
}
Did you try it? What happened? Was it what you wanted to happen? Did you notice your computer's fans speeding up, to handle all the heat from your CPU in a tight, "do-nothing" loop?
Fact is, you should not "pause" a background task in the first place; if you don't it to keep running, interrupt it. If you want to be able to resume later, provide a mechanism to allow that. Even having your thread blocked efficiently waiting on a WaitHandle object would be the wrong thing to do, because it wastes a thread pool thread.
The code you've posted here is about the worst way to implement "pausing". Instead of waiting on some synchronization object such as a WaitHandle, you have the current thread just loop without interrupting, constantly checking the value of a flag. Even ignoring the question of whether you're using volatile (the code example doesn't show that, but then it also wouldn't compile, so…), it's terrible to force a CPU core to do so much work and yet get nowhere.
Don't pause your BackgroundWorker.DoWork handler in the first place. Really. Just don't do that. But if you insist, then at least use some kind of waitable object instead of a "spin-wait" loop as in the example you've posted here.
Here's an example of how your code might work if you wanted to avoid altogether tying up a thread while "paused". First, don't use BackgroundWorker, because it doesn't have a graceful way to do this. Second, do use await…that does specifically what you want: it allows the current method to return, but without losing track of its progress. The method will resume executing when the thing it waited on indicates completion.
In the example below, I've tried to guess at what the code that calls RunWorkerAsync() looks like. Or rather, I just assumed you've got a button2, which when clicked you call that method to start your worker task. If this is not enough to get you pointed in the right direction, please improve your question by including a good, minimal, complete code example showing what you're actually doing.
// These fields will work together to provide a way for the thread to interrupt
// itself temporarily without actually using a thread at all.
private TaskCompletionSource<object> _pause;
private readonly object _pauseLock = new object();
private void button2_Click(object sender, DoWorkEventArgs e)
{
// Initialize ProgressBar. Note: in your version of the code, this was
// done in the DoWork event handler, but that handler isn't executed in
// the UI thread, and so accessing a UI object like progressBar1 is not
// a good idea. If you got away with it, you were lucky.
progressBar1.Minimum = 0;
progressBar1.Maximum = 100000;
progressBar1.Value = 0;
// This object will perform the duty of the BackgroundWorker's
// ProgressChanged event and ReportProgress() method.
Progress<int> progress = new Progress<int>(i => progressBar1.Value++);
// We do want the code to run in the background. Use Task.Run() to accomplish that
Task.Run(async () =>
{
for (int i = 0; i < 100000; i++)
{
progress.Report(i);
Task task = null;
// Locking ensures that the two threads which may be interacting
// with the _pause object do not interfere with each other.
lock (_pauseLock)
{
if (i == 50000)
{
// We want to pause. But it's possible we lost the race with
// the user, who also just pressed the pause button. So
// only allocate a new TCS if there isn't already one
if (_pause == null)
{
_pause = new TaskCompletionSource<object>();
}
}
// If by the time we get here, there's a TCS to wait on, then
// set our local variable for the Task to wait on. In this way
// we resolve any other race that might occur between the time
// we checked the _pause object and then later tried to wait on it
if (_pause != null)
{
task = _pause.Task;
}
}
if (task != null)
{
// This is the most important part: using "await" tells the method to
// return, but in a way that will allow execution to resume later.
// That is, when the TCS's Task transitions to the completed state,
// this method will resume executing, using any available thread
// in the thread pool.
await task;
// Once we resume execution here, reset the TCS, to allow the pause
// to go back to pausing again.
lock (_pauseLock)
{
_pause.Dispose();
_pause = null;
}
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
lock (_pauseLock)
{
// A bit more complicated than toggling a flag, granted. But it achieves
// the desirable goal.
if (_pause == null)
{
// Creates the object to wait on. The worker thread will look for
// this and wait if it exists.
_pause = new TaskCompletionSource<object>();
}
else if (!_pause.Task.IsCompleted)
{
// Giving the TCS a result causes its corresponding Task to transition
// to the completed state, releasing any code that might be waiting
// on it.
_pause.SetResult(null);
}
}
}
Note that the above is just as contrived as your original example. If all you had really was a simple single loop variable iterating from 0 to 100,000 and stopping halfway through, nothing nearly so complicated as the above would be required. You'd just store the loop variable in a data structure somewhere, exit the running task thread, and then when you want to resume, pass in the current loop variable value so the method can resume at the right index.
But I'm assuming your real-world example is not so simple. And the above strategy will work for any stateful processing, with the compiler doing all the heavy-lifting of storing away intermediate state for you.
I have a log window in my application, when I have a few thousand logs, filtering them to include or exclude different log levels makes the UI unresponsive for a period of time from the work load. So I have tried to move the heavy lifting to a worker thread, and am having the same issue still.
I am using an ObservableCollection to hold the log information in my model, and I just reference that directly with my ViewModel. I have used BindingOperations.EnableCollectionSynchronization() to let my worker thread update my observable collection without Dispatching it to the UI thread.
I run the following to update the collection on a worker thread with a
Task.Run(new Action(() => FilterList()));
Methods:
private void FilterList()
{
//Necessary even with EnableCollectionSynchronization
App.Current.Dispatcher.Invoke(new Action(() =>
{
FilteredLogEvents.Clear();
}));
foreach (LogEvent log in FilterLogEvents())
{
FilteredLogEvents.Add(log);
}
RaisePropertyChanged("FilteredLogEvents");
FinishedFilteringLogs();
}
//Filters and returns a list of filtered log events
private List<LogEvent> FilterLogEvents()
{
List<LogEvent> selectedEvents = (from x in LogEvents
where ((ViewDebugLogs == true) ? x.Level == "Debug" : false)
|| ((ViewErrorLogs == true) ? x.Level == "Error" : false)
|| ((ViewInfoLogs == true) ? x.Level == "Info" : false)
select x).ToList();
return selectedEvents;
}
This causes the UI to freeze on the foreach. I also tried just newing up an ObservableCollection and then assigning FilteredLogEvents to it with FilteredLogEvents = myNewCollection; this also causes the UI to freeze for a short while during that process.
If I use Thread.Sleep(1) within the foreach loop the UI remains responsive, though this seems like an inelegant and hacky solution.
What do I need to do to make this work?
Edit: A bit more code context from this class (LogEntries)
The callback for FinishedFilteringLogsEventHandler goes back to the ViewModel to change a bool that enables a couple checkboxes when the filtering is complete.
//Constructor
public LogEntries()
{
foreach(NlogViewerTarget target in NLog.LogManager.Configuration.AllTargets.Where(t=>t is NlogViewerTarget).Cast<NlogViewerTarget>())
{
target.RecieveLog += RecieveLog;
}
FilteredLogEvents = new ObservableCollection<LogEvent>();
BindingOperations.EnableCollectionSynchronization(FilteredLogEvents, filteredLogEventsLock);
}
public delegate void FinishedFilteringLogsEvent();
public FinishedFilteringLogsEvent FinishedFilteringLogsEventHandler;
private object filteredLogEventsLock = new object();
public ObservableCollection<LogEvent> FilteredLogEvents { get; set; }
Some thoughts to consider to improve the speed and responsiveness of your code
A few days ago I asked a similar question and someone advised me not to use the threadpool for long running Tasks. The thread pool is a collection of available threads, that can be started swiftly in comparison to starting a traditional thread like System.ComponentModel.BackGroundWorker.
Although it takes more time to create and start a real thread, this is no problem for long running tasks.
The number of threads in the thread pool is limited, so better not use it for longer running tasks.
If you run a task, it is only scheduled to run in the near future when a thread is available. If all threads are busy it will take some time before the thread starts.
The change from Task to Backgroundworker is limited. If you really want to stick to tasks, consider creating an async function:
async void FilteredLogEvents.AddRangeAsync(IEnumerable<LogEvent> logEvents)
or maybe better:
async void FilteredLogEvents.SetAsync(IEnumerable<LogEvent> logEvents)
which does the clear and add in one async call.
Make your own function async:
private async void FilterList()
{
var filteredLogEvents = FilterLogEvents();
var myTask = Task.Run( () => FilteredLogEvents.SetAsync(filteredLogEvents);
// if desired do other things.
// wait until ready:
await myTask();
RaisePropertyChanged("FilteredLogEvents");
FinishedFilteringLogs();
}
By the way: Are you sure that your sequence of logEvents does not change while you are filtering it?
If so, why do you use ToList() instead of returning an IEnumerable and use deferred execution?
If you are not certain: what happens if during the FilterLogEvents your sequence of logEvents changes?
I'm working with some code, that has an approach I've never dealt with before, and am hoping someone might be able to provide a spark of knowledge:
Effectively the class is setup like this:
void Loop()
{
for (int i = 0; i < 100; i++)
{
//need to block calls to this until UpdateComplete is hit
Update(i);
}
}
void Update(int i)
{
//asynchronous - returns immediately
}
void UpdateComplete()//called from another thread
{
//unblock further calls to Update();
}
The big caveat is that it's not possible to call Update() before UpdateComplete() has 'returned' or been calledback.
Ignoring any UI side effects, is there a neat solution to this problem?
I've currently got two strategies - one hacky and one I feel is over-complicated:
1 - Hacky: Set a global class boolean IsBlocked, that Update() sets to true and UpdateComplete() sets to false, and inside my for loop (in Loop()), just put in a while (IsBlocked){}.
2 - Over-complicated: Get rid of the loop altogether. Call Update(0); from inside Loop() instead, and have UpdateComplete() call Update(1);, Update(2); and so on.
I guess I'm hoping for something like a Thread.Pause that can then be remotely 'resumed' by another thread (the separate one that calls UpdateComplete()
Any ideas appreciated!
Async/await can also be used here
TaskCompletionSource<object> tcs = null;
async void Loop()
{
for (int i = 0; i < 100; i++)
{
tcs = new TaskCompletionSource<object>();
Update(i);
await tcs.Task;
}
}
void Update(int i)
{
//asynchronous - returns immediately
}
void UpdateComplete()//called from another thread
{
tcs.TrySetResult(null);
//unblock further calls to Update();
}
Post the updates into the thread pool using QueueUserWorkItem
Have a counter of the number of posted items.
Have main thread wait on AutoResetEvent
When Each update finishes decrement the count under lock.
When the count hits 0 Set the AutoResetEvent to awake the main thread
I didn't post any code cos I assumed you can google how to do each bit, if not comment and I can add code
I'm confused why I can't make this test counter application work with 2 (or more) simultaneous running countertextboxes with the use of "BeginInvoke" on my Dispatcher in the Count() method.
You can solve the issue by replacing the BeginInvoke by an Invoke. But this doesn't solve my confusion.
Here's the sample code I'm talking about:
public class CounterTextBox : TextBox
{
private int _number;
public void Start()
{
(new Action(Count)).BeginInvoke(null, null);
}
private void Count()
{
while (true)
{
if (_number++ > 10000) _number = 0;
this.Dispatcher.BeginInvoke(new Action(UpdateText), System.Windows.Threading.DispatcherPriority.Background, null);
}
}
private void UpdateText()
{
this.Text = "" + _number;
}
}
When you use Dispatcher.BeginInvoke it means that it schedules the given action for execution in the UI thread at a later point in time, and then returns control to allow the current thread to continue executing. Invoke blocks the caller until the scheduled action finishes.
When you use BeginInvoke your loop is going to run super fast since BeginInvoke returns right away. This means that you're adding lot and lots of actions to the message queue. You're adding them much faster than they can actually be processed. This means that there's a long time between when you schedule a message and when it actually gets a chance to be run.
The actual action that you're running uses the field _number. But _number is being modified by the other thread very quickly and while the action is in the queue. This means that it won't display the value of _number at the time you scheduled the action, but rather what it is after it has been continuing on in it's very tight loop.
If you use Dispatcher.Invoke instead then it prevents the loop from "getting ahead of itself" and having multiple scheduled events, which ensures that the value that it's writing is always the "current" value. Additionally, by forcing each iteration of the loop to wait for the message to be run it makes the loop a lot less "tight", so it can't run as quickly in general.
If you want to use BeginInvoke the first thing you really need to do is slow down your loop. If you want it to update the text every second, or ever 10ms, or whatever, then you can use Thread.Sleep to wait the appropriate amount of time.
Next, you need to take a copy of _number before passing it to the Dispatcher so that it displays the value at the time you scheduled it, not at the time it is executed:
while (true)
{
if (_number++ > 10000)
_number = 0;
int copy = _number;
this.Dispatcher.BeginInvoke(new Action(() => UpdateText(copy))
, System.Windows.Threading.DispatcherPriority.Background, null);
Thread.Sleep(200);
}
private void UpdateText(int number)
{
this.Text = number.ToString();
}
What's the best way to thread work (methods) in c#?
For example:
Let's say I have a form and want to load data from db.
My form controls:
- dataGridView (to show data from DB),
- label (loading status) and
- button (start loading).
When I click the button my form is frozen until the task is done. Also the loading status does not change until task is done. I think async threading would be the answer?
So my question: what's the best way to handle this? I know there is a lot stuff about Threading, but what's the difference between them and how do you make it thread safe?
How do you solve this kind of problems?
Best Regards.
If using Windows Forms, you should look at BackrgroundWorker. More generally, it is often useful to use the ThreadPool class. And finally, it is worth to take a look at the new .NET 4's Parallel class.
There is no universal 'best' way to thread work. You just have to try different ways of doing things, I'm afraid.
I particularly like Jeremy D. Miller's continuation idea described at this page (scroll down to find the "continuations" section). It's really elegant and means writing very little boilerplate code.
Basically, when you call "ExecuteWithContinuation" with a Func argument, the function is executed asynchronously, then returns an action when it finishes. The action is then marshalled back onto your UI thread to act as a continuation. This allows you to quickly split your operations into two bits:
Perform long running operation that shouldn't block the UI
... when finished, update the UI on the UI thread
It takes a bit of getting used to, but it's pretty cool.
public class AsyncCommandExecutor : ICommandExecutor
{
private readonly SynchronizationContext m_context;
public AsyncCommandExecutor(SynchronizationContext context)
{
if (context == null) throw new ArgumentNullException("context");
m_context = context;
}
public void Execute(Action command)
{
ThreadPool.QueueUserWorkItem(o => command());
}
public void ExecuteWithContinuation(Func<Action> command)
{
ThreadPool.QueueUserWorkItem(o =>
{
var continuation = command();
m_context.Send(x => continuation(), null);
});
}
}
You'd then use it like this (forgive the formatting...)
public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished()
{
DisableUi();
m_commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
ConnectToServer();
// This is the continuation that will be run
// on the UI thread
return () =>
{
EnableUi();
};
});
}
You can use this kind of pattern:-
private void RefreshButton_Click(object sender, EventArgs e)
{
MessageLabel.Text = "Working...";
RefreshButton.Enabled = false;
ThreadPool.QueueUserWorkItem(delegate(object state)
{
// do work here
// e.g.
object datasource = GetData();
this.Invoke((Action<object>)delegate(object obj)
{
// gridview should also be accessed in UI thread
// e.g.
MyGridView.DataSource = obj;
MessageLabel.Text = "Done.";
RefreshButton.Enabled = true;
}, datasource);
});
}
You cannot access your controls from the code that runs in the spun-off thread - the framework does not allow this, which explains the error you are getting.
You need to cache the data retrieved from the db in a non-forms object and populate your UI with data from that object after the background worker thread is done (and handle synchronization for access to that object).