Unresponsive UI While Waiting - c#

Final edit: Problem turned out to be unrelated to this async implementation. The first answer helped me move stuff around enough to look at the issue with a fresh set of eyes. Thanks, guys.
I'm talking with an IP camera in my app, both through httpclient (to request and receive the image) and a websocket (via websocket-sharp, to receive data).
Right now I'm just opening the websocket and asking for one image (loops will come later). Asking for the image is the last thing I do.
When I define the request for the image as
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
BMPReadyEventArgs args = new BMPReadyEventArgs();
args.BMP = blah2;
BitmapReady(this, args);
the app runs through all the code and freezes up. If I leave the ConfigureAwait term off, the await will surrender control back to the UI code, it will reach the end of said code, freeze for a couple seconds and then load the image.
With configureawait(false) on there it will load the image and then freeze up. I think that the stream I get from the request starts immediately so if it doesn't have to wait for context (?) it essentially runs synchronously. To be honest, I still don't really understand what configureawait actually does, or what the context people talk about actually means, but this behavior makes me think that the UI freezing has nothing to do with the async nature of the code.
I can't see the debugger jumping to an unexpected place by stepping through with F11 (though this seems to have some shortcomings when used with asynchronous code), it really does seem to just reach the end of the code and freeze up for a couple of seconds.
This leads me to a couple questions.
Will the UI always freeze up when the end of code is reached and, if so, do I need to make some sort of UI refresh ticker?
And, alternatively but more nebulously,
Is there an issue with my async implementation that could be causing this freeze?
Edit: Complete method:
public async void DownloadJPG()
{
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
BMPReadyEventArgs args = new BMPReadyEventArgs();
args.BMP = blah2;
BitmapReady(this, args);
}
called from
private async void HoldingPattern()
{
textBox1.Text = wsmanager.passer;
connector.BitmapReady += (sender, e) =>
pictureBox1.Image = e.BMP;
connector.DownloadJPG();
}
edit2: event handler:
public event EventHandler<BMPReadyEventArgs> BitmapReady;
BMPReadyEventArgs
class BMPReadyEventArgs:EventArgs
{
public Bitmap BMP {get;set;}
}

Your approach is way to complicate
public async Task<Image> DownloadJPGAsync()
{
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
return blah2;
}
Now from an async event handler
public async void Button1_Click()
{
var image = await connector.DownloadJPGAsync();
pictureBox1.Image = image;
}

Related

Which to use, WebClient.OpenRead, OpenReadAsync or OpenReadTaskAsync

I'm trying to understand the differences among WebClient.OpenRead, WebClient.OpenReadAsync and WebClient.OpenReadTaskAsync.
It looks like these have differences regarding blocking a thread, but I don't understand it well.
Could you please explain the differences? It would be great if you could give me some example (examples don't have to be sample code, but would be great if you could provide)
As you said, the difference is in thread blocking behavior. First one (OpenRead()) is thread blocking operation, other two - not. For example, let assume, that your network latency to reach google.com is 300ms. When you do var stream = webClient.OpenRead(#"https://www.google.com"); your application is "paused" for this 300ms, so code next to this line is not executed until your webClient return a stream to stream variable. This is calling Thread blocking.
When you do this in UI-thread (in example: in a button click handler) - your application become freezing and not responding to user actions. This is bad user experience, so never-ever call a thread blocking stuff in your UI. Here is example for console application:
var address = #"https://www.google.com/";
Console.WriteLine($"Opening stream from {address}");
using (var stream = webClient.OpenRead(address)) // this will block for 300ms
{
Console.WriteLine("Stream is open!");
// some other code
}
Second method (OpenReadAsync()) is asynchronous and return nothing immediately after a call, so your thread is not blocked. After awhile (300ms) OpenReadCompleted event will be raised by your webClient and all attached listeners will handle opened stream one-by-one. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
_client.OpenReadCompleted += OpenReadCompletedHandler;
}
private void ButtonClickHandler(object sender, EventArgs e)
{
_client.OpenReadAsync(#"https://www.google.com/");
}
private void OpenReadCompletedHandler(object sender, OpenReadCompletedEventArgs e)
{
// this event will be raiesed 300ms after 'Button' click
var stream = e.Result; // <- here is your stream
// some other code
}
}
The last one (OpenReadTaskAsync()) is all about TPL (Task Parallel Library) and async/await keywords. It runs all stuff in a Task which is returned by this method. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
}
private async void ButtonClickHandler(object sender, EventArgs e)
{
// after 'await' keyword, execution will be returned from this method immediately
// meanwhile, actual acquiring of 'stream' is running in a background thread
using (var stream = await _client.OpenReadTaskAsync(#"https://www.google.com/"))
{
// after 300ms, this code will be continued in UI thread
// result will be automaticly unpacked to 'stream' variable
// some other code
}
}
}
Hope this helps.
I'd suggest WebClient is more or less obsolete now, HttpClient is more appropriate for anything targeting .NET framework 4.5+ or .NET core. Just watch out that the latter does not automatically throw exceptions on HTTP error codes (400+).

Wait for BackgroundTask to complete

I have a BackgroundTask which generates some text and then saves it as a file to a LocalFolder. I need to get this file with my main project (same VS solution) after it's been generated by the BackgroundTask and do some next work with it. The background task is triggered both manually by the user (via a "Reload" button) and every 15 mins by a TimeTrigger.
This is the relevant code snippet:
syncTrigger.RequestAsync();
articles = await getCachedArticles("");
How can I tell the getCachedArticles method to wait until after previous request finishes before running? Thank you very much!
If I understand it correctly, what you need to do is to wait for the BackgroundTaskRegistration.Completed event to fire.
One approach would be to create an extension method that returns a Task that completes when the event fires:
public static Task<BackgroundTaskCompletedEventArgs> CompletedAsync(
this BackgroundTaskRegistration registration)
{
var tcs = new TaskCompletionSource<BackgroundTaskCompletedEventArgs>();
BackgroundTaskCompletedEventHandler handler = (s, e) =>
{
tcs.SetResult(e);
registration.Completed -= handler;
};
registration.Completed += handler;
return tcs.Task;
}
You would then use it like this:
var taskCompleted = registration.CompletedAsync();
await syncTrigger.RequestAsync();
await taskCompleted;
articles = await getCachedArticles("");
Note that the code calls CompletedAsync() before calling RequestAsync() to make sure the even handler is registered before the task is triggered, to avoid the race condition where the task completes before the handler is registered.
Whenever you want your current context to wait for an asynchronous method to complete before continuing, you want to use the await keyword:
await syncTrigger.RequestAsync(); //the line below will not be executed until syncTrigger.RequestAsync() completes its task
articles = await getCachedArticles("");
I would recommend reading through the await C# Reference Article in order to get the full picture of how it works.
EDIT: svick's answer shows the best approach, I even wrote a blog post about it. Below is my original answer with a couple of indirect alternatives that might work for some cases.
As others have noted, awaiting syncTrigger.RequestAsync() won't help, although it is a good idea nevertheless. It will resume execution when the background task was succesfully triggered, and as such allow you to check if it failed for any reason.
You could create an app service to make it work when you are triggering the background task from the application. App services behave similar to web services. They are running in a background task, but have request-response semantics.
In the background service you would need to handle the RequestReceived event:
public void Run(IBackgroundTaskInstance taskInstance)
{
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender,
AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
ValueSet arguments = args.Request.Message;
ValueSet result = new ValueSet();
// read values from arguments
// do the processing
// put data for the caller in result
await args.Request.SendResponseAsync(result);
messageDeferral.Complete();
}
In the client you can then call the app service:
var inventoryService = new AppServiceConnection();
inventoryService.AppServiceName = "from manifest";
inventoryService.PackageFamilyName = "from manifest";
var status = await inventoryService.OpenAsync();
var arguments = new ValueSet();
// set the arguments
var response = await inventoryService.SendMessageAsync(arguments);
if (response.Status == AppServiceResponseStatus.Success)
{
var result = response.Message;
// read data from the result
}
Check the linked page for more information about app services.
You could call the same app service from the scheduled background task as well, but there would be no way to get notified when the processing was completed in this case.
Since you've mentioned that you're exchanging data via a file in LocalFolder your application could try monitoring the changes to that file instead:
private async Task Init()
{
var storageFolder = ApplicationData.Current.LocalFolder;
var monitor = storageFolder.CreateFileQuery();
monitor.ContentsChanged += MonitorContentsChanged;
var files = await monitor.GetFilesAsync();
}
private void MonitorContentsChanged(IStorageQueryResultBase sender, object args)
{
// react to the file change - should mean the background task completed
}
As far as I know you can only monitor for all the changes in a folder and can't really determine what changed inside the event handler, so for your case it would be best to have a separate sub folder containing only the file saved by the background task once it completes. This way the event would only get raised when you need it to.
You'll have to check for yourself whether this approach works reliably enough for you, though.
You need to wait the RequestAsync method completed with the result
https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.background.devicetriggerresult
after that I suggest to wait a few seconds with task.delay and try to get the data.
Update:
When you have the device trigger result you need to check this result before to try to get the data after that you can suppose that you have the data saved. I suggested before to use Task.Delay just to wait a few seconds to be sure all data is saved because sometimes the process take some milliseconds more than is expected. I did this because we don't have an event like TriggerCompleted I needed to create my own approach.
I did this before in my app and it works very well.

Async method runs fine, but does not change anything

I want to read a XML file from the Web with following method.
public static async void Load_WinPhone(string URL)
{
HttpClient client = new HttpClient();
var httpResponseMessage = await client.GetAsync(new Uri(URL));
if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var xmlStream = await httpResponseMessage.Content.ReadAsStreamAsync();
XDocument Xdoc = XDocument.Load(xmlStream);
var query = from data in Xdoc.Descendants("article")
select new MyClass
{
Title = data.Element("title").Value
}
foreach (MyClass x in query)
{
AnotherClass.List.Add(x);
}
}
This Works, but after the method finished the AnotherClass.List is still empty.
I think it is because of the async, I tried this in the console without the async and it worked fine.
But now i want to to this on a Windows Phone 8.1 and the list stays empty.
Can someone explain me why or even have a workaround for this?
Yes, that's how await works - from the point of view of the caller, it's basically the same thing as a return. So when you call this method, it most likely returns on the first await - long before AnotherClass.List is modified.
The main problem you have is that your method is async void - you're throwing away all the information about the method's execution. Instead, you want to return Task - this allows you to await the method or bind a continuation to it.
Whenever you break the await chain, you also break the synchronicity of the code. Most of the time (especially in UI), you want to await all the way to the top - usually, the only thing that's async void is the event handlers, and even then it's only because event handlers must return void.
Overall, multi-threading and asynchronous code is a rather big topic - http://www.albahari.com/threading/ is a great start on understanding most of the fundamentals, as well as ways to handle it well in C#.

WebClient Timeout takes longer than expected (Using: Rx, Try Catch, Task)

Problem: I inherited WebClient in ExtendedWebClient where I override the WebRequest's timeout property in the GetWebRequest method. If I set it to 100ms, or even 20ms, it always takes up to more than 30 seconds at least. Sometimes it seems to not get through at all.
Also, when the service (see code below) serving the images comes back online again, the code written in Rx / System.Reactive does not push images into the pictureBox anymore?
How can I get around this, what am I doing wrong? (See code below)
Test case: I have a WinForms test project set up for this, which is doing the following.
GetNextImageAsync
public async Task<Image> GetNextImageAsync()
{
Image image = default(Image);
try {
using (var webClient = new ExtendedWebClient()) {
var data = await webClient.DownloadDataTaskAsync(new Uri("http://SOMEIPADDRESS/x/y/GetJpegImage.cgi"));
image = ByteArrayToImage(data);
return image;
}
} catch {
return image;
}
}
ExtendedWebClient
private class ExtendedWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var webRequest = base.GetWebRequest(address);
webRequest.Timeout = 100;
return webRequest;
}
}
3.1 Presentation code (using Rx)
Note: It has actually never reached the "else" statement in the pictures.Subscribe() body.
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
;
pictures.Subscribe(img => {
if (img != null) {
pictureBox1.Image = img;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
3.2 Presentation code (using Task.Run)
Note 1: Here the "else" body is getting called, though WebClient still takes longer than expected to timeout....
Note 2: I don't want to use this method, because this way I can't "Throttle" the image stream, I'm not able to get them in proper order, and do other stuff with my stream of images... But this is just example code of it working...
Task.Run(() => {
while (true) {
GetNextImageAsync().ContinueWith(img => {
if(img.Result != null) {
pictureBox1.Image = img.Result;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
}
});
As reference, the code to tranfser the byte[] to the Image object.
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using(var memoryStream = new MemoryStream(byteArrayIn)){
Image returnImage = Image.FromStream(memoryStream);
return returnImage;
}
}
The Other Problem...
I'll address cancellation below, but there is also a misunderstanding of the behaviour of the following code, which is going to cause issues regardless of the cancellation problem:
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
You probably think that the Throttle here will limit the invocation rate of GetNextImageAsync. Sadly that is not the case. Consider the following code:
var xs = Observable.Return(1)
.Throttle(TimeSpan.FromSeconds(5));
How long do you think it will take the OnNext(1) to be invoked on a subscriber? If you thought 5 seconds, you'd be wrong. Since Observable.Return sends an OnCompleted immediately after its OnNext(1) the Throttle deduces that there are no more events that could possibly throttle the OnNext(1) and it emits it immediately.
Contrast with this code where we create a non-terminating stream:
var xs = Observable.Never<int>().StartWith(1)
.Throttle(TimeSpan.FromSeconds(5));
Now the OnNext(1) arrives after 5 seconds.
The upshot of all this is that your indefinite Repeat is going to batter your code, requesting images as fast as they arrive - how exactly this is causing the effects you are witnessing would take further analysis.
There are several constructions to limit the rate of querying, depending on your requirements. One is to simply append an empty delay to your result:
var xs = Observable.FromAsync(GetValueAsync)
.Concat(Observable.Never<int>()
.Timeout(TimeSpan.FromSeconds(5),
Observable.Empty<int>()))
.Repeat();
Here you would replace int with the type returned by GetValueAsync.
Cancellation
As #StephenCleary observed, setting the Timeout property of WebRequest will only work on synchronous requests. Having looked at this necessary changes to implement cancellation cleanly with WebClient, I have to concur it's such a faff with WebClient you are far better off converting to HttpClient if at all possible.
Sadly, even then the "easy" methods to pull back data such as GetByteArrayAsync don't (for some bizarre reason) have an overload accepting a CancellationToken.
If you do use HttpClient then one option for timeout handling is via the Rx like this:
void Main()
{
Observable.FromAsync(GetNextImageAsync)
.Timeout(TimeSpan.FromSeconds(1), Observable.Empty<byte[]>())
.Subscribe(Console.WriteLine);
}
public async Task<byte[]> GetNextImageAsync(CancellationToken ct)
{
using(var wc = new HttpClient())
{
var response = await wc.GetAsync(new Uri("http://www.google.com"),
HttpCompletionOption.ResponseContentRead, ct);
return await response.Content.ReadAsByteArrayAsync();
}
}
Here I have used the Timeout operator to cause an empty stream to be emitted in the event of timeout - other options are available depending on what you need.
When Timeout does timeout it will cancel it's subscription to FromAsync which in turn will cancel the cancellation token it passes indirectly to HttpClient.GetAsync via GetNextImageAsync.
You could use a similar construction to call Abort on a WebRequest too, but as I said, it's a lot more of a faff given there's no direct support for cancellation tokens.
To quote the MSDN docs:
The Timeout property affects only synchronous requests made with the GetResponse method. To time out asynchronous requests, use the Abort method.
You could mess around with the Abort method, but it's easier to convert from WebClient to HttpClient, which was designed with asynchronous operations in mind.

Progress info using HttpClient

I wanted to implement progress bar using 'Windows.Web.Http.HttpClient' in Windows Phone 8.1 Silverlight.
Edit: First Attempt:
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute)).Progress = Progress;
// here i want to stop the execution till whole content downloaded
// Reason to wait here
// this client download will be used by one more async method. so i need to wait here till the file completely downloaded.
// this whole is done in this method only ( requirement)
}
private void Progress(IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> asyncInfo, HttpProgress progressInfo)
{
// here i getting the progress value.
// i have already set a call back method that report the progress on the UI.
}
One more attempt: But getting the Exception Invocation of delegate at wrong time.
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute))
df.Progress = Progress;
await df;
// here i want to stop the execution till whole content downloaded
}
Question:
All, I wanted is to stop the CheckProgress() Method to keep waiting till the whole download completed.
I got the solution of my problem but it is just a little miss in my second attempt:
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute))
df.Progress = (res, progress) =>
{
// no separate event handler.
}
await df;
}
It is just working fine.

Categories

Resources