I would like someone to explain to me what is Device.BeginInvokeOnMainThread and what is it for?
And also some examples of cases where it's used.
Just to add an example.
Imagine you have an async method DoAnyWorkAsync if you call it (just as an example) this way:
DoAnyWorkAsync().ContinueWith ((arg) => {
StatusLabel.Text = "Async operation completed...";
});
StatusLabel is a label you have in the XAML.
The code above will not show the message in the label once the async operation had finished, because the callback is in another thread different than the UI thread and because of that it cannot modify the UI.
If the same code you update it a bit, just enclosing the StatusLabel text update within Device.BeginInvokeOnMainThread like this:
DoAnyWorkAsync().ContinueWith ((arg) => {
Device.BeginInvokeOnMainThread (() => {
StatusLabel.Text = "Async operation completed...";
});
});
there will not be any problem.
Try it yourself, replacing DoAnyWorkAsync() with Task.Delay(2000).
The simple answer is: Background thread cannot modify UI elements because most UI operations in iOS and Android are not thread-safe; therefore, you need to invoke UI thread to execute the code that modifies UI such MyLabel.Text="New Text".
The detailed answer can be found in Xamarin document:
For iOS:
IOSPlatformServices.BeginInvokeOnMainThread() Method simply calls NSRunLoop.Main.BeginInvokeOnMainThread
public void BeginInvokeOnMainThread(Action action)
{
NSRunLoop.Main.BeginInvokeOnMainThread(action.Invoke);
}
https://developer.xamarin.com/api/member/Foundation.NSObject.BeginInvokeOnMainThread/p/ObjCRuntime.Selector/Foundation.NSObject/
You use this method from a thread to invoke the code in the specified object that is exposed with the specified selector in the UI thread. This is required for most operations that affect UIKit or AppKit as neither one of those APIs is thread safe.
The code is executed when the main thread goes back to its main loop for processing events.
For Android:
Many People think on Xamarin.Android BeginInvokeOnMainThread() method use Activity.runOnUiThread(), BUT this is NOT the case, and there is a difference between using runOnUiThread() and Handler.Post():
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);//<-- post message delays action until UI thread is scheduled to handle messages
} else {
action.run();//<--action is executed immediately if current running thread is UI thread.
}
}
The actual implementation of Xamarin.Android BeginInvokeOnMainThread() method can be found in AndroidPlatformServices.cs class
public void BeginInvokeOnMainThread(Action action)
{
if (s_handler == null || s_handler.Looper != Looper.MainLooper)
{
s_handler = new Handler(Looper.MainLooper);
}
s_handler.Post(action);
}
https://developer.android.com/reference/android/os/Handler.html#post(java.lang.Runnable)
As you can see, you action code is not executed immediately by Handler.Post(action). It is added to the Looper's message queue, and is handled when the UI thread's scheduled to handle its message.
You can only update the UI from the main UI thread. If you are running code on a background thread and need to update the UI, BeginInvokeOnMainThread() allows you to force your code to run on the main thread, so you can update the UI.
As explained above, any UI updates must happen in the main thread or an exception will occur.
Though there's a peculiarity with Xamarin.Forms, one can manilpulate UI elements (e.g. create Labels and add them to StackLayout's Children collection) off the main thread without any failures as long as this part of UI is detached from UI elements currently displayed. This approach can be used to boost performance by creating Xamarin.Forms controls and setting their child/parent relations in-memory/off-screen in a separate thread BUT in order to attach them to displayed container (e.g. assign ContentPage's Content property) you will have to do this in Device.BeginInvokeOnMainThread().
While analysing the relationship between UI thread and background thread in some situation, we should be aware of the following:
BeginInvokeOnMainThread method as described in the docs, merely queues the invocation and returns immediately to the caller. So in this case, UI thread and background thread which submitted some work to UI thread, might work in parallel.
However, there is also InvokeOnMainThread which, as described in the docs, waits for the UI thread to execute the method, and does not return until the code pointed by action has completed. So in this case, background thread waits for UI thread to finish executing the given work, and then background thread continues execution.
Related
What is the recommended way to do multithreading with MVVM Light.
I have a model which has a bool property Busy
public bool Busy
{
get { return busy_; }
set
{
Set(nameof(Busy), ref busy_, value, broadcast: true);
}
}
My view model publish the model directly for the view (the model is inherit MVVM Light's ViewModelBase), so the view binds directly to the model's busy property.
If I call the model always from the UI thread everything is good. But if I do the following in my view model so it may execute on a different thread
Task.Factory.StartNew(() =>
{
model_.SomeFunctionThatWillSetBusyDuringItsExecution();
});
Then of course Busy is set from a non UI thread and then the binding fails and the application crashes. If I happen to use the Messenger in the property setter, it seems the Messenger does not automatically dispatch the Messenger handler code to the UI thread either.
I realized there is a DispatcherHelper in MVVM Light, but for the binding it does not seem to help. If I change the property to
public bool Busy
{
get { return busy_; }
set
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
Set(nameof(Busy), ref busy_, value, broadcast: true);
});
}
}
I still get an exception and the application crash due to the binding source is not on the correct thread. So my question is simple, what is the recommended way to do multithreading like this in MVVM Light?
I did also try to use a syncronizationContext.
syncContext_.Post(() =>
{
Set(nameof(Busy), ref busy_, value, broadcast: true);
}, null);
That works if the call is always from a non UI-thread. If the call is already from the UI thread, the syncContext.Post results in that the Set() function is not called until all the code in the ViewModel method has finished. That means the busy state might not be updated correctly for the remaining code. So it is not an ideal solution.
I am thankful for help on this topic.
Instead of adding the DispatcherHelper code inside the property I added it at all places where the property was modified. Then it seems to work well.
Only problem, since one dispatch the work to the UI thread, the code in the ViewModel would not get the updated state if part of the view model method already runs on the UI thread. I found a way to force the UI thread to process its messenger queue though making sure it got the updated state of Busy. It is not the best looking solution, and it is likely to have a bad performance impact due to all context switching, but at least it works and it is a simple one liner.
Code to force the UI thread to process all messages in its queue
DispatcherHelper.UIDispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
If there is a more optimal way to solve it then please let me know. Otherwise I will set this as the answer in a few days from now.
I have this simple code:
Parallel.Invoke(
() => picturebox_1.Refresh(),
() => picturebox_2.Refresh());
and I'm getting this:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.
How can I resolve this issue? I just want to run the refresh in parallel, the refresh method runs the Paint Event which has code to render an image...
Thanks!
You must invoke using the UI thread, you cannot update the UI on a thread that you created, you must use the forms invoke method or the controls...
I'm not very experienced with this topic so forgive me if this isn't very clear.
I've created a Portable Class Library that has an ObservableCollection of Sections, and each secion has an ObservableCollection of Items.
Both of these collections are bound to the UI of separate Win8 and WP8 apps.
I'm trying to figure out the correct way to populate these collections correctly so that the UI gets updated from the PCL class.
If the class was inside the win8 project I know I could do something like Dispatcher.BeginInvoke, but this doesn't translate to the PCL, nor would I be able to reuse that in the WP8 project.
In this thread (Portable class library equivalent of Dispatcher.Invoke or Dispatcher.RunAsync) I discovered the SynchroniationContext class.
I passed in a reference to the main app's SynchroniationContext, and when I populate the sections I can do so because it's only the one object being updated:
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the CollectionChanged event on the current thread
UpdateSections(sections);
}
else
{
// Post the CollectionChanged event on the creator thread
_synchronizationContext.Post(UpdateSections, sections);
}
However, when I try to do the same thing with articles, I have to have a reference to both the section AND the article, but the Post method only allows me to pass in a single object.
I attempted to use a lambda expression:
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the CollectionChanged event on the current thread
section.Items.Add(item);
}
else
{
// Post the CollectionChanged event on the creator thread
_synchronizationContext.Post((e) =>
{
section.Items.Add(item);
}, null);
}
but I'm guessing this is not correct as I'm getting an error about being "marshalled for a different thread".
So where am I going wrong here? how can I update both collections correctly from the PCL so that both apps can also update their UI?
many thanks!
Hard to say without seeing the rest of the code but I doubt is has anything to do with Portable Class Libraries. It would be good to see the details about the exception (type, message and stack trace).
The way you call Post() with more than argument looks correct. What happens if you remove the if check and simply always go through SynchronizationContext.Post()?
BTW: I don't explicitly pass in the SynchronizationContext. I assume that the ViewModel is created on the UI Thread. This allows me to capture it like this:
public class MyViewModel
{
private SynchronizationContext _context = SynchronizationContext.Current;
}
I would recommend that at least in your ViewModels, all publicly observable state changes (ie property change notifications and modifications to ObservableCollections) happen on the UI thread. I’d recommend doing the same thing with your model state changes, but it might make sense to let them make changes on different threads and marshal those changes to the UI thread in your ViewModels.
To do this, of course, you need to be able to switch to the UI thread in portable code. If SynchronizationContext isn’t working for you, then just create your own abstraction for the dispatcher (ie IRunOnUIThread).
The reason you were getting the "marshalled on a different thread" error is that you weren't passing the item to add to the list as the "state" object on the Post(action, state) method.
Your code should look like this:
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the CollectionChanged event on the current thread
section.Items.Add(item);
}
else
{
// Post the CollectionChanged event on the creator thread
_synchronizationContext.Post((e) =>
{
var item = (YourItemnType) e;
section.Items.Add(item);
}, item);
}
If you make that change, your code will work fine from a PCL.
Here is my problem , I have created a SortableCollection : ObservableCollection
and added a sort method (sort colors).
When I sort The collection with the principal Thread , it works every thing is fine and works
But When I try to sort this customCollection by using an item in the collection I have an expetion : (The calling thread cannot access this object because a different thread owns it).
I have looked in web and I found several solution , One Solution
This type of solution put the collection multithread for insertion , removing moving operation.
But not for the custom sort.
Thanks for help,
WPF classes have thread affinity. What this means is that all changes to those objects must be in the same thread where they were created. It truly is difficult to create a user interface API that is thread-safe, so Microsoft chose to keep it singlethreaded and force run-time checking to make sure of it.
That said, there are a few options you have to perform your sort in a background thread, and then apply it in the UI thread. The first option is to copy your SortableCollection into a plain old List or Array and perform the sort in the background. Once the background thread is complete, you use a Dispatcher to execute code in the UI thread. Every UI element in WPF extends System.Windows.Threading.DispatcherObject and most extend System.Windows.Freezable. The DispatcherObject is where you get the Dispatcher to execute code in the UI thread.
Logically, the execution would be something like this:
public void BackgroundSort()
{
List<T> items = new List<T>(this.ToArray());
BackgroundSortDelegate del = Sort;
del.BeginInvoke(SortCompleted, del);
}
private void SortCompleted(IAsyncResult result)
{
BackgroundSortDelegate del = result.AsyncState as BackgroundSortDelegate;
List<T> items = del.EndInvoke(result);
this.Dispatcher.Invoke(()=>{this.Collection = items;});
}
The short explanation of what happened is that the background worker/delegate is using a copy of the items in this list. Once the sort is complete, we are calling the Dispatcher object and invoking an action. In that action we are assigning the new sorted list back to our object.
The key to assigning the result of any background work within the UI thread is to use the UI's Dispatcher object. There's actually probably a half dozen ways to invoke a background worker in C#, but the approach to get your work in a background thread into the UI thread is the same.
I'm using a WebClient to get infos asynchronously from my web service :
wc.DownloadStringCompleted += DownloadStringCompleted;
wc.DownloadStringAsync(service);
I works fine, but I think the DownloadStringCompleted method is working on the UI Thread, and since i'm doing a lot of parsing there, my page takes a few seconds to appear. However, since I have so fixed data and a progress bar, I would'nt mind have it appearing instantly.
How could I perfom this ?
Thanks !
Use HttpWebRequest rather than WebClient. HWR doesn't return on the UI thread and so doesn't block it from updating.
WebClient does indeed return on the UI thread so yes your parsing will be blocking the UI. For perf reasons it is recommended that you use HttpWebRequest instead.
With HttpWebRequest your event will fire on the background thread so you can do all the processing you need, however you then have the problem of marshaling the results back to the UI thread so that you can update the UI (otherwise you will see cross thread violation exceptions). You can use the Dispatcher to marshal the results back to the UI with a method like the following:
private void UpdateUI(Results results)
{
if (!Deployment.Current.Dispatcher.CheckAccess())
Deployment.Current.Dispatcher.BeginInvoke(() => UpdateUI(results));
else
{
//Update the UI
{
}
In Mango the WebClient is changed so that if the
wc.DownloadStringAsync(service);
call is made from the background thread the response also comes to the background thread. You can use the BackgroundWorker to achieve this.