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.
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 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.
Trying to push a message into UI and receive some result to return in synchronous way from web-service.
Method code goes as follows.
[OperationContract]
public string DecypherCaptcha(string captcha)
{
var connection = new HubConnection("http://localhost:51806");
IHubProxy hub = connection.CreateHubProxy("robo");
string decaptcha = null;
hub.On("captchaDecyphered", decyphered =>
{
decaptcha = decyphered;
});
connection.Start().Wait();
hub.Invoke<string>("DecypherCaptcha", new object[] { captcha });
return decaptcha;
}
The issue is that method finishes before value is obtained from hub's captchaDecyphered. However the expression { decaptcha = decyphered; } triggers fine from server after method exits.
Adding ManualResetEvent flag and WaitOne() for it doesn't solve the problem freezing the execution and preventing hub.On("captchaDecyphered" from firing.
Any ideas how to synchronize this?
UPDATE#1 Small notice. Cannot avoid using the intermediate synchronous WCF web-service acting as SignalR client, because of pretty specific robots sitting behind, which are able to interact with outer world only by calling webservices synchronously. Basically in this scenario when robot faces captcha it calls the web-service passing it via SignalR to UI for manual recognition.
UPDATE#2 Thanks to #Ken's inspiring advice got it working by enclosing the connection establishing and hub method invocation into separate 'Thread' followed by waiting with 'ManualResetEvent':
new Thread(() =>
{
connection.Start().Wait();
hub.Invoke<string>("DecypherCaptcha", new object[] { captcha });
}).Start();
sync.WaitOne();
Have previously been trying to start from 'Task' supposing it would run on separate thread implicitly, but with no luck.
You could have the DecypherCaptcha hub method on the SignalR server return the deciphered captcha as a Task<string> instead on invoking captchaDecyphered.
You may want to use a TaskCompletionSource to help you create the appropriate task. Basically you could call tcs.SetResult(deciphered) and return tcs.Task instead of calling Clients.Caller.captchaDecyphered(deciphered).
Then your client-side code code would simply be:
[OperationContract]
public string DecypherCaptcha(string captcha)
{
var connection = new HubConnection("http://localhost:51806");
IHubProxy hub = connection.CreateHubProxy("robo");
connection.Start().Wait();
return hub.Invoke<string>("DecypherCaptcha", captcha).Result;
}
You've got several options.
(1) Spin off the request to the SignalR hub onto a separate thread, probably using the static ThreadPool class, and then add in all the ManualResetEvent stuff. That way it won't block when you're waiting on the SignalR method to return.
(2) Make the DecypherCaptcha method asynchronous. It looks to me like the DecypherCaptcha() is intended to be a WCF method that in turn wraps a SignalR method. If that's the case, forgetting for a moment whether this is a wise approach, you could still call a WCF method on the client when the captchaDecyphered SignalR method completes. But if it's not intended to be a WCF method, then you could have DecypherCaptcha() either (a) return a Task<T>, and only flag the Task to be complete when the captchaDecyphered completes; or (b) pass in a Func<T> as a continuation parameter, and call that when the captchaDecyphered completes.
In general, one of the things that makes asynchronous programming difficult is that except for the very top-level method, you generally need to make every method that calls an asynchronous method itself asynchronous, all the way up and down the stack, either through the Async pattern (nasty), or continuation passing (better) or through a Task object + async/await (probably best). So adding in a single asynchronous method often results in significant changes to your application, all the way through. That's one of the many reasons why the new async and await keywords in .NET 4.5 are so helpful, because they help to encapsulate the necessary changes when you start making your application asynchronous.
You can use the generic Invoke method where you can specify the type of result you expect. With the method you CAN use .Result to wait for the result.
string result = IHubProxy.Invoke<string>("GetString").Result;
How can you make a background web request and then update the UI, but have all the code that does the web requesting/parsing in a separate class so you can use it in multiple places? I thought I could use the classes methods as event handlers for a BackgroundWorker class, like
APIHelper mHelper = new APIHelper("http://example.com?foo=bar");
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork +=new DoWorkEventHandler(mHelper.GetResponse);
bw.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(mHelper.HandleResponse);
bw.RunWorkerAsync();
where APIHelper has the method
public void GetResponse(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker) sender;
WebRequest request = HttpWebRequest.Create(this.URL);
IAsyncResult result = (IAsyncResult)
request.BeginGetResponse(ResponseCallback, request);
}
but then I don't know how to access the worker thread from ResponseCallback and, anyway, HandleResponse gets called first (obviously). (I tried putting in result.AsyncWaitHandle.WaitOne(); but I get a NotSupportedException error.) Yet I can't work out how to make the web request call synchronously. I'm clearly trying to go about this the wrong way, but I have no idea what the right way is.
ETA:
My aim is to be able to go:
user clicks (a) button(s) (on various pages)
a "working" message is displayed on the UI thread (and then input is blocked)
in a background thread my APIHelper class makes the relevant API call, gets the response, and passes it back to the UI thread; I only seem to be able to do this by starting another thread and waiting for that to return, because there's no synchronous web requests
the UI thread updates with the returned message (and input continues as before)
I can do the first two bits, and if I have the response, I can do the last bits, but I can't work out how to do the middle bit. Hopefully that made it clearer!
It took me several tried before I found there is a Dispatcher.
During the BackgroundWorker's dowork and complete methods you can call:
this.Dispatcher.BeginInvoke(() =>
{
// UPDATE UI BITS
});
I think the Dispatcher is only available in the view. So I'm not sure if the methods can exist outside of the xaml.cs
Put whatever you want to update in your UI; when updating an ObservableCollection you must do the update of you items in the Dispatcher.BeginInvoke too
This link might be a good read too:
http://www.windowsphonegeek.com/articles/All-about-Splash-Screens-in-WP7-ndash-Creating-animated-Splash-Screen
Update to assist notes
This is just a rough idea mind you...
bw.DoWork +=new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(Complete)
// At least I think the EA is DoWork....
public void DoWork(object sender, DoWorkEventArgs e)
{
mHelper.GetResponse();
this.Dispatcher.BeginInvoke(() =>
{
UIObject.Visibility Collapse.
});
// Wait and do work with response.
});
}
public void Complete(object sender, RunWorkerCompleteEventArgs e)
{
this.Dispatcher.BeginInvoke(() =>
{
UIObject.Visible ....
});
}
I'd put all this logic in a viewmodel that the viewmodel of each page inherits from.
Have the pages bind to properties on the viewmodel (such as ShowLoading, etc.) which the model updates appropriately. i.e. before making the webrequest and in the callback.
As you won't be running the viewmodel code in the UI thread you also wouldn't need to run in a separate BackgroundWorker and you'll be able to access the properties of the viewmodel without issue.
It might be useful if you use a helper class that I have developed for WebDownload purposes during WP7 development.
I'm using it in 2-3 WP7 apps and no problem so far. Give it a go to see if it helps. You can get the class from the my blog linked bellow:
http://www.manorey.net/mohblog/?p=17#content
[NOTE] When working with this class you don't need to run anything in a background worker or new thread; it handles it all asynchronously.
I have a Silverlight application that uses WCF services and also uses the Wintellect Power Threading library to ensure logic executes fully before the application continues. This is achieved by calling back to the application using delegates so it can continue after the service call has completely finished.
I wish to achieve the same thing in another part of my application but without the use of callbacks e.g. call method that uses WCF service to say load an object from the database, wait for this to return and then return the Id of the object from the original method called.
The only way I could see to do this was to carry out the call to the WCF service in a helper library which loads the object on a different thread and the original method would keep checking the helper library (using static variables) to wait for it to complete and then return it.
Is this the best way to achieve this functionality? If so here are details of my implementation which is not working correctly.
public class MyHelper
{
private static Thread _thread;
private static User _loadedObject;
public static GetUser()
{
return _loadedObject;
}
public static void LoadObject(int userId)
{
_loadedObject = null;
ParameterizedThreadStart ts = new ParameterizedThreadStart(DoWork);
_thread = new Thread(ts);
_thread.Start(userId);
}
private static void DoWork(object parameter)
{
var ae = new AsyncEnumerator();
ae.BeginExecute(DoWorkWorker(ae, Convert.ToInt32(parameter)), ae.EndExecute);
}
private static IEnumerator<Int32> DoWorkWorker(AsyncEnumerator ae, int userId)
{
// Create a service using a helper method
var service = ServiceHelper.GetService<IUserServiceAsync>();
service.BeginGetUserById(userId, ae.End(), null);
yield return 1;
_loadedObject = service.EndGetUserById(ae.DequeueAsyncResult());
_thread.Abort();
}
}
My method then is:
public int GetUser(int userId)
{
MyHelper.LoadObject(userId);
User user = MyHelper.GetUser();
while (user == null)
{
Thread.Sleep(1000);
user = MyHelper.GetUser();
}
return user.Id;
}
The call to the get the user is executed on a different thread in the helper method but never returns. Perhaps this is due to the yield and the calling method sleeping. I have checked the call to get the user is on a different thread so I think everything should be kept separate,
The whole construct you are using does not match current best practices of Silverlight. In Silverlight your data access methods (via WebServices of course) are executed asynchronously. You should not design around that, but adapt your design accordingly.
However calling services sequentially (which is different than synchonously) can be valid in some scenarios. In this blog post I have shown how to achieve this by subscribing the Completed event of the remote call and block the UI in the meantime, with which the workflow looks and feels like normal async calls.
I believe calls to the server from Silverlight apps use events that fire on the UI thread; I think that's part of the Silverlight host environment in the browser and can't be worked around. So trying to call back to the server from another thread is never going to end well. If you are waiting in program code in the UI thread, your never going to get the call result events from your WCF calls.
You can simulate a synchronous call from a non-UI thread with a callback on the UI thread, but that is probably not what you want. It's better to bite the bullet and make your program logic work with the async calls Silverlight gives you.
If you code against the Interface created for your service reference you can call the Begin and End methods 'synchronously' for each one of your service calls, we then pass in an Action<T> to execute after the End methods has completed. Take note that you have to do this from a dispatcher. This is very close to making a synchronous call as the code to run after the call is still written where the call is made, and it executes after the service call is completed. It does however involve creating wrapper methods but we also worked around that by hiding our wrappers and generating them automatically. Seems like a lot of work but isn't, and ends up being more elegant than all the event handlers etc. Let me know if you need more info on this pattern