Is it necessary to lock code snippet where multiple threads access same wpf component via dispatcher?
Example:
void ladder_OnIndexCompleted(object sender, EventArgs args)
{
lock (locker)
{
pbLadder.Dispatcher.Invoke(new Action(() => { pbLadder.Value++; }));
}
}
pbLadder is a progress bar and this event can be raised from multiple threads in the same time.
You should not acquire a lock if you're then going to marshal to another thread in a synchronous fashion - otherwise if you try to acquire the same lock in the other thread (the dispatcher thread in this case) you'll end up with a deadlock.
If pbLadder.Value is only used from the UI thread, then you don't need to worry about locking for thread safety - the fact that all the actions occur on the same thread isolates you from a lot of the normal multi-threading problems. The fact that the original action which caused the code using pbLadder.Value to execute occurred on a different thread is irrelevant.
All actions executed on the Dispatcher are queued up and executed in sequence on the UI thread. This means that data races like that increment cannot occur. The Invoke method itself is thread-safe, so also adding the action to the queue does not require any locking.
From MSDN:
Executes the specified delegate with the specified arguments
synchronously on the thread the Dispatcher is associated with.
and:
The operation is added to the event queue of the Dispatcher at the
specified DispatcherPriority.
Even though this one is pretty old it was at the top of my search results and I'm pretty new (4 months since I graduated) so after reading other peoples comments, I went and spoke with my senior coder. What the others are saying above is accurate but I felt the answers didn't provide a solution, just information. Here's the feedback from my senior coder:
"It's true that the Dispatcher is running on its own thread, but if another thread is accessing an object that the dispatcher wants to access then all UI processing stops while the dispatcher waits for the access. To solve this ideally, you want to make a copy of the object that the dispatcher needs to access and pass that to the dispatcher, then the dispatcher is free to edit the object and won't have to wait on the other thread to release their lock."
Cheers!
Related
I am supposed to work on a wpf legacy application( and desktop app is a new beast for me).
I have read that consumming task should not be launched on the ui thread : but I find this following code in the code behind of a view :
bool isSearching = true;
try
{
Task<ProductSearchResult>.Factory
.StartNew(() => DBCatalogService.Search( search.Criteria, search.CriteriaPage, search.CriteriaResultByPage)
.ContinueWith(res => LoadResult(res, search.Criteria, search.CriteriaPage, search.CriteriaResultByPage),
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(s => isSearching = false);
}
catch
{
...
}
I am wondering it will not cause any trouble.
I know that it's sounds weird to call the database directly from the view code behind, but I just want to know if it could freeze the ui thread or something like this.
Thank you for your advice on this matter.
My question is : does the sample code that I provided would block the UI thread and have to be considered harmfull or not ?
The call to the DBCatalogService.Search method will not block the UI thread since it is being invoked on a background thread using the task parallel library (TPL).
The call to the LoadResult method will however be executed on the UI thread once the task that calls the Search method has completed.
This is fine though since this method probably sets some properties of some UI elements based on the result of the search and you must do this on the UI thread. This is because WPF controls have thread affinity, meaning that a control can only be accessed on the thread on which it was originally created.
So no, the sample code you have provided should not be considered "harmfull" in terms of UI responsiveness assuming that the LoadResult doesn't perform any strange and potentially long-running operations.
If you block the UI thread (dispatcher thread) with a long-running operation such as a synchronous DB request, your application will be unresponsive until the thread is unblocked.
You can avoid this by either:
Doing the blocking/synchronous operation on another thread
Making the operation non-blocking/asynchronous
Both of the above
Using async/await can make your code read much like the synchronous form, but with asynchronous behaviour. It should be much clearer than the code sample you give in the question. However you need an async form of your search.
If you do use another thread, remember to dispatch back onto the UI thread if you have to update UI properties.
I checked which thread my Dispose(bool) methods get called on. When the app is running, it is always the UI thread that calls Dispose, say when clicking on the [x] to close a Form. But when I close the whole app, many Dispose methods get called on a (single) different thread. When I dump the stack trace, I see that they all get called from
System.ComponentModel.Component.Finalize().
Does that mean all my Dispose methods need to be made thread-safe? Or is WinForms guaranteeing that the UI thread won't touch these objects any more and does it also establish some kind of "happened-before" relationship between the UI thread and the one that's now finalizing?
Yes, the finalizer works on a separate thread. Usually this is no problem, because when an Object is finalized it is not reachable by any user thread (like the UI thread) anymore. So, you usually do not have to be thread-safe within your finalizer.
Acording to my understanding Dispatcher.Invoke and Dispatcher.BeginInvoke executes on UI thread, The only difference is That Invoke is synchronous and BeginInvoke is asynchronous.My problem is when i use this code
EDisc.App.Current.Dispatcher.
Invoke(
DispatcherPriority.Normal, new Action(delegate
{
context = NavigationManager.CurrentPage.DataContext;
}));
Value of context is returned. However with the below code
EDisc.App.Current.Dispatcher.
BeginInvoke(
DispatcherPriority.Normal, new Action(delegate
{
context = NavigationManager.CurrentPage.DataContext;
}));
Context is null and i get an InvalidOperation Exception saying "
The calling thread cannot access this object because a different thread owns it.I am calling this from a WCF service which is executing with UseSynchronizationContext = false .Can anybody explain this behaviour?
Both BeginInvoke and Invoke will end up calling an internal method called BeginInvokeImpl to do the work. The difference is that Invoke then waits for the operation to complete before returning.
And there's one other difference: if you are already on the UI thread and you're using DispatcherPriority.Send Invoke will actually invoke the method directly without going via BeginInvokeImpl, meaning that the operation is processed without going via the message queue. (If you're not using Send then any other messages already queued up with higher property than your operation will get processed first.)
But since you're presumably not on the UI thread here - you're on some WCF callback - that special case won't apply. So Invoke ends up calling into the same underlying implementation as BeginInvoke.
From the information you've provided, I'd have to guess that there's a missing detail somewhere here. The code you've shown should work fine, unless perhaps you have multiple UI threads in your application, and the page that happens to be in CurrentPage belongs to different threads from time to time.
If you do have multiple UI threads, then the approach you're using - pushing everything through the current Application object's dispatcher - isn't going to work, because you'll have multiple dispatchers. You'd need to get the right dispatcher for whichever UI element you're planning to touch.
Incidentally, one way you might accidentally end up with multiple UI threads is if you construct a UI object (e.g. a Page) on some worker thread or callback. Is it possible that you've done that somewhere?
Why i should call Control.Invoke from non-ui thread? As i know any manipulations with control are the messages to control. So when i call, for example TextBox.Text = "text", it will produce message SendMessage(TextBox.Hanlde...). That message will be queued into UI thread message queue and dispatched by UI thread. Why i have to call invoke, even it will produce the same message?
There are two reasons MS developers made this restriction:
Some UI functions have access to thread-local storage (TLS). Calling these functions from another thread gives incorrect results on TLS operations.
Calling all UI-related functions from the same thread automatically serializes them, this is thread-safe and doesn't require synchronization.
From our point of view, we just need to follow these rules.
Because you cannot directly access UI controls from threads other than the thread they were created on. Control.Invoke will marshal your call onto the correct thread - allowing you to make a call from another thread onto the UI thread without needing to know yourself what the UI thread is or how to perform the marshalling.
Update: to answer your question, you don't have to use Control.Invoke - if you have code to marshal your call onto the correct thread and post a message to the message pump - then use that. This, however, is known as re-inventing the wheel. Unless you are doing something that changes the behaviour.
In my WPF app I have a long running upload running, which raises progress events as it goes which updates a progress bar. The user also has a chance of cancelling the upload, or it might go wrong. These are all async events, so they need to be executed using Dispatcher.Invoke in order to update the UI.
So the code looks like this, ish:
void OnCancelButtonClicked(object sender, EventArgs e)
{
upload.Cancel();
_cancelled = true;
view.Close();
view.Dispose();
}
void OnProgressReceived(object sender, EventArgs<double> e)
{
Dispatcher.Invoke(() =>
{
if (!cancelled)
view.Progress = e.Value;
}
}
Supposing that setting view.Progress on a disposed view is going to throw an error, is this code thread safe? i.e. if a user clicks cancel while the progress is updating, he/she will have to wait until the progress has been updated, and if the progress is updated during execution of OnCancelButtonClicked, the Dispatcher.Invoke call will cause the view.Progress update to be queued til after _cancelled is set, so I won't get a problem there.
Or do I need a lock to be safe, a la:
object myLock = new object();
void OnCancelButtonClicked(object sender, EventArgs e)
{
lock(myLock)
{
upload.Cancel();
_cancelled = true;
view.Close();
view.Dispose();
}
}
void OnProgressReceived(object sender, EventArgs<double> e)
{
Dispatcher.Invoke(() =>
{
lock(myLock)
{
if (!cancelled)
view.Progress = e.Value;
}
}
}
You don't have to add a lock. Dispatcher.Invoke and BeginInvoke requests will not run in the middle of other code (that's the whole point of them).
Just two things to consider:
BeginInvoke may be more appropriate in this case, Invoke will queue the request and then block the calling thread until the UI thread becomes idle and finishes executing the code, BeginInvoke will only queue the request without blocking.
Some operations, especially operations that open windows (including message boxes) or do inter-process communication may allow the queued dispatcher operations to run.
EDIT: first, I don't have citations because the MSDN pages on the subject are unfortunately very low on details - but I have written test programs to check the behavior of BeginInvoke and everything I write here is the result of those tests.
Now, to expand on the second point we first need to understand what the dispatcher does. Obviously this is a very simplified explanation.
Any Windows UI works by processing messages; For example when the user moves the mouse over a window the system will send that window a WM_MOUSEMOVE message.
The system send the message by adding it a queue, each thread may have a queue, all windows created by the same thread share the same queue.
In the heart of every Windows program there's a loop called "message loop" or "message pump", this loop reads the next message from the queue and calls the appropriate window's code to process that message.
In WPF this loop and all the related processing handled by the Dispatcher.
An application can either be in the message loop waiting for the next message or it could be doing something. That is why when you have a long calculation all the thread's windows become unresponsive - the thread is busy working and doesn't return to the message loop to process the next message.
Dispatcher.Invoke and BeginInvoke works by queuing the requested operation and executing it the next time the thread returns to the message loop.
That is why Dispatcher.(Begin)Invoke can't "inject" code in the middle of your method, you won't get back to the message loop until your method returns.
BUT
Any code can run a message loop. When you call anything that runs a message loop the Dispatcher will be called and can run the (Begin)Invoke operations.
What kinds of code has a message loop?
Anything that has a GUI or that accepts user input, for example dialog boxes, message boxes, drag&drop etc. - if those didn't have a message loop then the app would have been unresponsive and unable to handle user input.
Inter-process communication that uses windows messages behind the scenes (most inter-process communication methods, including COM, use them).
Anything else that takes a long time and doesn't freeze the system (the fast that the system isn't frozen is proof it's processing messages).
So, to summarize:
the Dispatcher can't just drop code into your thread, it can only execute code when the application is in the "message loop".
Any code you write doesn't have message loops unless you explicitly wrote them.
Most UI code doesn't have it's own message loop, for example if you call Window.Show and then do some long calculation the window will only appear after the calculation is finished and the method returns (and the app returns to the message loop and processes all the messages required to open and draw a window).
But any code that interacts with the user before it returns (MessageBox.Show, Window.ShowDialog) has to have a message loop.
Some communication code (network and inter-process) uses message loops, some doesn't, depending on the specific implementation you are using.
This is an interesting question. Items executed in the dispatcher are queued and execute on the same thread as UI interaction. Here's the best article on this subject:
http://msdn.microsoft.com/en-us/library/ms741870.aspx
If I were to venture a guess, I would say that Dispatcher.Invoke(Action) probably queues an atomic work item, so it's likely going to be ok, however I'm not sure if it wraps up your UI event handler in an atomic action item for instance:
//Are these bits atomic? Not sure.
upload.Cancel();
_cancelled = true;
For safety's sake I would personally lock, but your question warrants more research. Might need a dive in reflector to figure out for sure.
As an aside, I'd probably optimize your lock a bit.
Dispatcher.Invoke(() =>
{
if (!cancelled)
{
lock(myLock)
{
if(!cancelled)
view.Progress = e.Value;
}
}
}
But that's probably overkill :)