HttpClientt.SendAsync doesnt wait/lock execution - c#

var httpResponseMessage = await httpClient.SendAsync(message).ConfigureAwait(false);
var dataStream = await httpResponseMessage.Content.ReadAsStreamAsync();
This by idea should be awaited, but no matter what it do executions exists the method and returns to UI. Execution resumes when responses arrives, but by that time UI has already updated that execution finished, when in fact it hasn't.
All calling methods are awaited.
Initial method is not awaited by design (Task.Run(() => StartDownload(selectedSchedules)); which starts UI method executing services that triggers httpclient, when that call finished UI should update with progress, but the second httpClient.SendAsyncis executed, execution returns to UI
Task.Run(() => StartDownload(selectedSchedules)); //First call, initiated by a button
public async Task StartDownload(SchedulesList list)
{
//var t = new Task(() => _scheduleServices.Download(list));
//t.Start();
//await t;
await _scheduleServices.DownloadIwcfDb(list, UsbInfoModels);
}
public async Task Download(SchedulesList schedulesList)
{
await DownloadDb(schedulesList);
}
private async Task DownloadDb(SchedulesList schedulesList)
{
using (var httpClient = new HttpClient())
{
var message = new HttpRequestMessage(new HttpMethod("POST"), ApiCallUrls.GetIwcfSchedules)
{
Content = new StringContent(JsonConvert.SerializeObject(schedulesList), Encoding.UTF8, "application/json")
};
httpClient.Timeout = TimeSpan.FromMinutes(20);
var httpResponseMessage= await httpClient.SendAsync(message).ConfigureAwait(false);
var dataStream = await httpResponseMessage.Content.ReadAsStreamAsync();
using (Stream contentStream = dataStream, stream = new FileStream(Path.Combine(Directories.SomEDir, Directories.SomeFileName), FileMode.Create, FileAccess.Write, FileShare.None))
{
await contentStream.CopyToAsync(stream);
}
}
}
Call Chain Added, irrelevant code removed from the methods

You're problem probably lies within your first call.
In your code you have:
Task.Run(()=>StartDownload(selectedSchedules)); //First call, initiated by a button
//I assume afterwards you update the ProgressBar or give some other progress feedback
What this does is: It calls StartDownload and immediately continues execution. All the other stuff (downloading etc) is then happening in the background. Remember that the method StartDownload does not block; it simply returns a Task object. If you do not await that Task object, the code will simply proceed.
I guess what you wanted is: Call StartDownload, wait for it to finish, then update the progress.
A quick solution would be to mark your event handler of the button with async, and then use async all the way. The method would look a bit like this:
private async void HandleEvent()
{
await StartDownload();
//update progress
}
I can recommend you this blog post from Stephen Cleary for an introduction to async-await: https://blog.stephencleary.com/2012/02/async-and-await.html

Related

Run HttpClient PostAsync in another Class from MainWindow Background worker?

Preface: I'm a hobbyist so pardon my ignorance please ;-)
I'm trying to perform a web based multifunction task (online astrometry plate solving) that is located in a separate Class from my main window. I would like to do this in the background to keep the main window active (for logging messages in a scrolling LogTextBox).
In my Main Window I call this:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Astrometry ast = new Astrometry();
ast.OnlineSolve(GlobalVariables.SolveImage);
}
And in the Astrometry Class it stops at the await httpClient.PostAsync(...
returns to main window and nothing else happens
class Astrometry
{
public void OnlineSolve(string image)
{
GetSession(apikey);
}
private async void GetSession(string apikey)
{
...misc code
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpContent contentPost = new StringContent(input, Encoding.UTF8, "application/x-www-form-urlencoded");
using (var response = await httpClient.PostAsync(baseAddress, contentPost))
{
string responseData = await response.Content.ReadAsStringAsync();
...more stuff
}
}
I'm wondering if this is just not possible to do this way...
Thanks in advance!
When using async/await, you must make sure to always propagate the tasks.
public async Task OnlineSolve(string image)
{
await GetSession(apikey).ConfigureAwait(false);
}
private async Task GetSession(string apikey)
{
...misc code
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpContent contentPost = new StringContent(input, Encoding.UTF8, "application/x-www-form-urlencoded");
using (var response = await httpClient.PostAsync(baseAddress, contentPost).ConfigureAwait(false))
{
string responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
...more stuff
}
}
}
and again in the worker:
public async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Astrometry ast = new Astrometry();
await ast.OnlineSolve(GlobalVariables.SolveImage);
}
The main rule is don't break the await chain. As soon as you break it, you lose track of the asynchronous operation, and you lose your operation to wait for the result and handle any errors.
Also note that since you're using pure asynchronous I/O, you don't really need to use a background worker in the first place. Just run the asynchronous method directly from your button click handler or whatever, and you'll be fine. If you need to do some CPU work as well, Task.Run is a decent option. I'm not even sure if background worker handles marshalling the await back to the proper thread correctly (EDIT: And indeed, it doesn't; so there really isn't any point in using BackgroundWorker with await, it's just confusing - just use Task.Run for CPU work, and await for asynchronous I/O).
The thing to keep in mind is that await basically functions as a magical return (similar to a yield return, if you've ever used that). As soon as execution reaches an await that awaits a task that isn't complete, the method returns a Task (if possible). That's the point where the caller gets flow control back - and if you don't use await in the caller, that's the state the work is in when your caller continues execution. Sometimes, this is desirable - for example, when launching multiple tasks in parallel. Usually, you just want to await all asynchronous methods immediately.

Calling async method on button click

I created Windows Phone 8.1 project and I am trying to run async method GetResponse<T>(string url) on button click and waiting for the method to finish, but method is never finishing. Here is my code:
private void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
task.Wait();
var items = task.Result; //break point here
}
public static async Task<List<T>> GetResponse<T>(string url)
{
List<T> items = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
try
{
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = strReader.ReadToEnd();
items = JsonConvert.DeserializeObject<List<T>>(text);
}
catch (WebException)
{
throw;
}
return items;
}
It will hang on task.Wait().
I changed my button click method to async and used await before the async method and I get the result(await GetResponse<string>("url")). What is wrong with Task<List<string>> task = GetResponse<string>("url")?
What am I doing wrong?
Thanks for the help!
You're the victim of the classic deadlock. task.Wait() or task.Result is a blocking call in UI thread which causes the deadlock.
Don't block in the UI thread. Never do it. Just await it.
private async void Button_Click(object sender, RoutedEventArgs
{
var task = GetResponseAsync<MyObject>("my url");
var items = await task;
}
Btw, why are you catching the WebException and throwing it back? It would be better if you simply don't catch it. Both are same.
Also I can see you're mixing the asynchronous code with synchronous code inside the GetResponse method. StreamReader.ReadToEnd is a blocking call --you should be using StreamReader.ReadToEndAsync.
Also use "Async" suffix to methods which returns a Task or asynchronous to follow the TAP("Task based Asynchronous Pattern") convention as Jon says.
Your method should look something like the following when you've addressed all the above concerns.
public static async Task<List<T>> GetResponseAsync<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = await strReader.ReadToEndAsync();
return JsonConvert.DeserializeObject<List<T>>(text);
}
This is what's killing you:
task.Wait();
That's blocking the UI thread until the task has completed - but the task is an async method which is going to try to get back to the UI thread after it "pauses" and awaits an async result. It can't do that, because you're blocking the UI thread...
There's nothing in your code which really looks like it needs to be on the UI thread anyway, but assuming you really do want it there, you should use:
private async void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
var items = await task;
// Presumably use items here
}
Or just:
private async void Button_Click(object sender, RoutedEventArgs
{
var items = await GetResponse<MyObject>("my url");
// Presumably use items here
}
Now instead of blocking until the task has completed, the Button_Click method will return after scheduling a continuation to fire when the task has completed. (That's how async/await works, basically.)
Note that I would also rename GetResponse to GetResponseAsync for clarity.
#ChrisWalsh: If you use Task.Run() and call the async task inside that function, the task will run on a new UI thread and prevent blocking your UI.
use below code
Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));

How to cancel an async call without checking the cancellation pending property (Instantly terminating the method call)

I can't find a way to cancel an async call running on the background thread.
Let's say I have something like this:
private async void myMethod()
{
String result = await Task.Run(async () => await myTimeConsumingMethod());
//Do stuff with my result string...
}
Here I'm calling an async method on my background thread, and since I don't have any loops I can't do something like this:
for (i = 0; i < someBigNumber; i++)
{
token.ThrowIfCancellationRequested();
await doSomeWork();
}
Instead I have a single call to an async method that can take more than 10 seconds to complete, I can't check if the token has been canceled inside that method, since I'm awaiting for the async call to complete.
This is what I'm trying to achieve:
private async void myMethod()
{
String result = await Task.Run(async () => await myTimeConsumingMethod());
//Override the HardwareButtons.BackPressed event (I already have a method for that)
//When the await is completed, the String is the result of the async
//call if the method has completed, otherwise the result String
//should be set to null.
}
The problem is that I don't know what code to use inside the HardwareButtons.BackPressed event to terminate my async call.
I mean, I literally want to "terminate its process" and make that Task.Run instantly return null.
Is there a way to do that?
This is the implementation of myTimeConsumingMethod():
public static async Task<String> ToBase64(this StorageFile bitmap)
{
IRandomAccessStream imageStream = await CompressImageAsync(bitmap);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
PixelDataProvider pixels = await decoder.GetPixelDataAsync();
byte[] bytes = pixels.DetachPixelData();
return await ToBase64(bytes, (uint)decoder.PixelWidth, (uint)decoder.PixelHeight, decoder.DpiX, decoder.DpiY);
}
This is impossible. You can create a Task that will complete when that existing operation completes, or mark itself as cancelled if a cancellation token is cancelled, but that won't actually stop the original operation, merely allow the program to continue executing despite the fact that the operation isn't actually done yet.
See this blog article for more information on the subject.

Async/Await and Task<T> does not return data

When I do this it doesn't return Task<TokenModel> I mean this line is freezing tokenModel = await response.Content.ReadAsAsync<TokenModel>();
Foo()
{
var r = IsLoginOk();
}
public async Task<TokenModel> IsLoginOk()
{
var handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
handler.PreAuthenticate = true;
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
client = new HttpClient(handler);
client.BaseAddress = new Uri(Properties.Settings.Default.WebApiUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var apiAccountLogin = apiManager.ApiAccountLogin(Properties.Settings.Default.WebApiPassword, Properties.Settings.Default.WebApiUsername);
var response = await client.PostAsJsonAsync(apiAccountLogin.Url, apiAccountLogin.Container);
response.EnsureSuccessStatusCode();
tokenModel = await response.Content.ReadAsAsync<TokenModel>();
return tokenModel;
}
But if I use void then it is working fine
Foo()
{
IsLoginOk();
}
public async void IsLoginOk()
{
var handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
handler.PreAuthenticate = true;
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
client = new HttpClient(handler);
client.BaseAddress = new Uri(Properties.Settings.Default.WebApiUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var apiAccountLogin = apiManager.ApiAccountLogin(Properties.Settings.Default.WebApiPassword, Properties.Settings.Default.WebApiUsername);
var response = await client.PostAsJsonAsync(apiAccountLogin.Url, apiAccountLogin.Container);
response.EnsureSuccessStatusCode();
tokenModel = await response.Content.ReadAsAsync<TokenModel>();
}
As I see my code is like Microsift example code http://msdn.microsoft.com/en-us/library/hh191443.aspx .
Is this kind of some Deadlock?
I need to return tokenModel. How I can do this?
Thanks!
Change your constructor like this:
private Foo()
{
// Do only synchronous initialization here
}
public static async Task<Foo> CreateAsync()
{
var foo = new Foo();
await foo.IsLoginOk();
return foo;
}
Your callside needs to be changed accordingly.
I am not sure but your code should be like this
This is one way to do it
public async void Foo()
{
var r = await IsLoginOk();
}
Way to do it make use of ConfigureAwait(false) like as below when making call.
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
For this behaviour I searched on Google and found this good article. Please read it to avoid future problems: Don't Block on Async Code
Reason of deadlock
if code is like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
that it hangs So this is what happens, starting with the top-level method (Button1_Click for UI):
The top-level method calls GetJsonAsync (within the UI context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
… Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete.
Your code is causing a deadlock that I explain on my blog. In short, when await pauses an async method, by default it will capture a context (e.g., a UI context or ASP.NET request context). Later, when the await completes, the async method is resumed in that context. However, if the calling code uses Task.Wait or Task<T>.Result to synchronously block on the async method, then it is blocking a thread within that context and the async method cannot complete.
The proper solution is to use async all the way, not use async void or ConfigureAwait(false):
async Task FooAsync()
{
var result = await IsLoginOkAsync();
}
public async Task<TokenModel> IsLoginOkAsync();
IsLoginOk() will return a Task.
To return a TokenModel alone, use
var r = IsLoginOk().Result;
If you don't wait for the result, execution will continue, as it is async. Also, Name should be IsLoginAsync()

What happens when HttpClient usages are not awaited

Given code similar to
Task.Run(() =>
{
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(urlToInvoke);
}
});
In a situation like this, it appears that GetAsync does not actually operate. Is the task canceled prior to completion or what is actually going on here?
Now if you change things slightly and insert
Task.Run(() =>
{
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(urlToInvoke);
Task.Delay(5000).Wait()
}
});
GetAsync does execute completely. What is going on here? Is Task.Delay affinitizing itself to the same Task that is inside responseTask ultimately making this equivalent to responseTask.Wait()?
You are thinking of it incorrectly. Here is pseudo version of what is happening inside the class.
class HttpClient : IDisposeable
{
private CancelationTokenSource _disposeCts;
public HttpClient()
{
_disposeCts = new CancelationTokenSource();
}
public Task<HttpResponseMessage> GetAsync(string url)
{
return GetAsync(url, CancellationToken.None);
}
public async Task<HttpResponseMessage> GetAsync(string url, CancelationToken token)
{
var combinedCts =
CancellationTokenSource.CreateLinkedTokenSource(token, _disposeCts.Token);
var tokenToUse = combinedCts.Token;
//... snipped code
//Some spot where it would good to check if we have canceled yet.
tokenToUse.ThrowIfCancellationRequested();
//... More snipped code;
return result;
}
public void Dispose()
{
_disposeCts.Cancel();
}
//... A whole bunch of other stuff.
}
The important thing to see is when you exit the using block a internal cancelation token is canceled.
In your first example the task had not finished yet so tokenToUse would now throw if ThrowIfCancellationRequested() was called.
In your second example the task had already finished so the act of canceling the internal token had no effect on the task that was returned due to it already reaching the completed state.
It is like asking why this causes the task to be canceled.
using (var client = new HttpClient())
{
var cts = new CancellationTokenSource()
var responseTask = client.GetAsync(urlToInvoke, cts.Token);
cts.Cancel();
}
but this does not
using (var client = new HttpClient())
{
var cts = new CancellationTokenSource()
var responseTask = client.GetAsync(urlToInvoke, cts.Token);
Task.Delay(5000).Wait()
cts.Cancel();
}
When you don't await (or Wait) tasks they do not cancel themselves. They continue to run until they reach one of three statuses:
RanToCompletion - Finished successfully.
Canceled - The cancellation token was canceled.
Faulted - An unhandled exception occurred inside the task.
In your case however, because no one waits for the task to complete, the using scope ends which disposes of the HttpClient. This in turn will cancel all the client's tasks, client.GetAsync(urlToInvoke) in this case. So that inner async task will end immediately and become Canceled, while the outer task (Task.Run) will simply end without doing anything.
When you use Task.Delay(5000).Wait() which is basically Thread.Sleep(5000) the task has a chance to complete before the using scope ends. That mode of operation however should be avoided. It blocks a thread throughout the Waitand could lead to deadlocks in single threaded SynchronizationContexts. This also hides possible exceptions in the task (which could tear down the application in earlier versions of .Net)
You should always wait for tasks to complete, preferably asynchronously, and as Servy commented, there's no reason to use Task.Run here for offloading because GetAsyncis asynchronous and won't block the calling thread.
using (var client = new HttpClient())
{
var response = await client.GetAsync(urlToInvoke);
}

Categories

Resources