Why is the original task canceled when it ContinueWith something else? - c#

It's been 4 weeks since I dived into C# programming. It's really fun, however, I got a pain in the ass:
When I start a Task with HttpClient.PostAsync() alone, it works fine. But if I continue with something else, the orginal Task will be canceled, not by me. Looks like the Task is not happy about being continued.
Task<HttpResponseMessage> task0;
Task task1;
using (var client = new HttpClient())
{
HttpContent content = new ByteArrayContent(new byte[]{});
task0 = client.PostAsync("<valid http address>", content);
task1 = task0.ContinueWith((t) =>
{
// Do nothing
});
}
task1.Wait();
// I got task0.IsCanceled == true here
I tried:
1, Add task0.wait() immediately after PostAsync() will solve the issue but it's not what I want. Because I need the performance benefit from async and doing that will make it totally sync.
2, Add task0.wait() before task1.wait() will cause a TaskCanceledExcpetion.
3, Remove task1 and wait on task0 will be OK.
4, Call task0.start() will got "Start may not be called on a promise-style task."
So, plz someone tell me what am I doing wrong?
PS:
Before I asked this question, I had googled it for days. And some questions from StackOverflow might look relevent, but it turned out they were not the same to mine.
Who canceled my Task? She/He was asking why the continuation task wasn't executed.
Why does TaskCanceledException occur? She/He was messing up with the CancellationToken which I never did and got unexpected result.
I've also read this Task Cancellation and still got no clue.

Your HttpClient most likely get's disposed before PostAsync is finished. Remove using statement for test purposes, and everything will work as expected. You should dispose your client at a different point, when request is finished.
Also, many would recommend reuse single instance of HttpClient as much as possible, if your application logic allows it.

So, this code appears to be disposing the HttpClient as soon as you hook up the continuation, which is most likely not what you want.
If you want to use a using statement to dispose your client instance, you need to use the async and await keywords. The following code is equivalent to your example, with the compiler hooking up the continuation for you.
public async Task FooAsync()
{
using (var client = new HttpClient())
{
HttpContent content = new ByteArrayContent(new byte[]{});
await client.PostAsync("<valid http address>", content);
// Continue your code here.
}
}
If you want to continue using continuations created without the compiler's help, you can put the disposing logic in a continuation:
Task<HttpResponseMessage> task0;
Task task1;
var client = new HttpClient();
HttpContent content = new ByteArrayContent(new byte[]{});
task0 = client.PostAsync("<valid http address>", content);
task1 = task0.ContinueWith((t) =>
{
client.Dispose();
})
task1.Wait();

Related

Now that we have an "await" keyword, is there any benefit to using ContinueWith method?

Picture the following code:
var client = new HttpClient();
var response = await client.GetAsync("www.someaddress.yo");
string content = await response.Content.ReadAsStringAsync();
Is there any added benefit, other than possibly saving a single thread, by writing the above code the following way:
var client = new HttpClient();
string content = await client.GetAsync("www.someaddress.yo")
.ContinueWith(r => r.Result.Content.ReadAsStringAsync()).Result;
Correct me if I'm wrong, but I believe performance-wise both codes end up doing the same amount of work.
The second snippet has no benefits, doesn't "save" any threads while allocating another task object and making debugging and exception handling harder by wrapping any exceptions in an AggregateException.
A task is a promise that something will produce some output in the future. That something may be :
A background operation running on a threadpool thread
A network/IO operation that doesn't run on any thread, waiting instead for the Network/IO driver to signal that the IO has finished.
A timer that signals a task after an interval. No kind of execution here.
A TaskCompletionSource that gets signalled after some time. No execution here either
HttpClient.GetAsync, HttpClient.GetStringAsync or Content.ReadAsStringAsync are such IO operations.
await doesn't make anything run asynchronously. It only awaits already executing tasks to complete without blocking.
Nothing is gained by using ContinueWith the way the second snippet does. This code simply allocates another task to wrap the task returned by ReadAsStringAsync. .Result returns the original task.
Should that method fail though, .Result will throw an AggregateException containing the original exception - or is it an AggregateException containing an AggregateException containing the original? I don't want to find out. Might as well have used Unwrap(). Finally, everything is still awaited.
The only difference is that the first snippet returns in the original synchronization context after each await. In a desktop application, that would be the UI. In many cases you want that - this allows you to update the UI with the response without any kind of marshalling. You can just write
var response = await client.GetAsync("www.someaddress.yo");
string content = await response.Content.ReadAsStringAsync();
textBox1.Text=content;
In other cases you may not want that, eg a library writer doesn't want the library to affect the client application. That's where ConfigureAwait(false) comes in :
var response = await client.GetAsync("www.someaddress.yo").ConfigureAwait(false);
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

SendAsync and CopyToAsync not working when downloading a large file

I have a small app that receives a request from a browser, copy the header received and the post data (or GET path) and send it to another endpoint.
It then waits for the result and sends it back to the browser. It works like a reverse proxy.
Everything works fine until it receives a request to download a large file. Something like a 30MB will cause an strange behaviour in the browser. When the browser reaches around 8MB it stops receiving data from my app and, after some time, it aborts the download. Everything else works just fine.
If I change the SendAsync line to use HttpCompletionOption.ResponseContentRead it works just fine. I am assuming there is something wrong waiting for the stream and/or task, but I can't figure out what is going on.
The application is written in C#, .net Core (latest version available).
Here is the code (partial)
private async Task SendHTTPResponse(HttpContext context, HttpResponseMessage responseMessage)
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
await responseStream.CopyToAsync(context.Response.Body);
}
}
public async Task ForwardRequestAsync(string toHost, HttpContext context)
{
var requestMessage = this.BuildHTTPRequestMessage(context);
var responseMessage = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted);
await this.SendHTTPResponse(context, responseMessage);
}
EDIT
Changed the SendHTTPResponse to wait for responseMessage.Content.ReadAsStreamAsync using await operator.
Just a guess but I believe the issue lies with the removal of the transfer encoding:
context.Response.Headers.Remove("transfer-encoding");
If the http request you are making with _httpClient returns the 30MB file using Chunked encoding (target server doesn't know the file size) then you would need to return the file to the browser with Chunked encoding as well.
When you buffer the response on your webservice (by passing HttpCompletionOption.ResponseContentRead) you know the exact message size you are sending back to the browser so the response works successfully.
I would check the response headers you get from responseMessage to see if the transfer encoding is chunked.
You are trying to stream a file but you are doing it not exactly right. If you do not specify,ResponseHeadersRead, response will never come back unless the server ends the request because it will try to read the response till the end.
HttpCompletionOption enumeration type has two members and one of them is ResponseHeadersRead which tells the HttpClient to only read the headers and then return back the result immediately.
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var stream = await response.Content.ReadAsStreamAsync();
using (var reader = new StreamReader(stream)) {
while (!reader.EndOfStream) {
//Oh baby we are streaming
//Do stuff copy to response stream etc..
}
}
Figure 3 shows a simple example where one method blocks on the result of an async method. This code will work just fine in a console application but will deadlock when called from a GUI or ASP.NET context. This behavior can be confusing, especially considering that stepping through the debugger implies that it’s the await that never completes. The actual cause of the deadlock is further up the call stack when Task.Wait is called.
Figure 3 A Common Deadlock Problem When Blocking on Async Code
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait();
}
}
The root cause of this deadlock is due to the way await handles contexts. By default, when an incomplete Task is awaited, the current “context” is captured and used to resume the method when the Task completes. This “context” is the current SynchronizationContext unless it’s null, in which case it’s the current TaskScheduler. GUI and ASP.NET applications have a SynchronizationContext that permits only one chunk of code to run at a time. When the await completes, it attempts to execute the remainder of the async method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the async method to complete. They’re each waiting for the other, causing a deadlock.
Note that console applications don’t cause this deadlock. They have a thread pool SynchronizationContext instead of a one-chunk-at-a-time SynchronizationContext, so when the await completes, it schedules the remainder of the async method on a thread pool thread. The method is able to complete, which completes its returned task, and there’s no deadlock. This difference in behavior can be confusing when programmers write a test console program, observe the partially async code work as expected, and then move the same code into a GUI or ASP.NET application, where it deadlocks.
The best solution to this problem is to allow async code to grow naturally through the codebase. If you follow this solution, you’ll see async code expand to its entry point, usually an event handler or controller action. Console applications can’t follow this solution fully because the Main method can’t be async. If the Main method were async, it could return before it completed, causing the program to end. Figure 4 demonstrates this exception to the guideline: The Main method for a console application is one of the few situations where code may block on an asynchronous method.
Figure 4 The Main Method May Call Task.Wait or Task.Result
class Program
{
static void Main()
{
MainAsync().Wait();
}
static async Task MainAsync()
{
try
{
// Asynchronous implementation.
await Task.Delay(1000);
}
catch (Exception ex)
{
// Handle exceptions.
}
}
}
LEARN MORE HERE
try these.
using (HttpResponseMessage responseMessage= await client.SendAsync(request))
{
await this.SendHTTPResponse(context, responseMessage);
}
or
using (HttpResponseMessage responseMessage=await _httpClient.SendAsync(requestMessage,
HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
await this.SendHTTPResponse(context, responseMessage)
}

Task using httpclient and returning a value not working correctly

I've looked around and followed a few tutorials on how to set this up and I'm having no luck. I'm building a cross platform app using Xamarin. I have a portable class library to handle all my business logic and web service calls.
The issue I'm having is when I run the code within the method below it works without an issue however if I run it as shown below as a task it takes a long time. I believe its deadlocked but as I'm new to this I can't understand why. I want my Api calls to be in the PCL so I can share it between each platfrom.
public async Task<LoginResponse> Login(string userName, string password)
{
HttpClient client = new HttpClient();
string baseUrl = "http://someURLhere/";
client.BaseAddress = new Uri(baseUrl);
string authData = string.Format("/IOSLogin?Username={0}&Password={1}&languageSettingOnDevice={2}", userName, password, "en");
var uri = new Uri(string.Format("{0}{1}", baseUrl, authData));
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
LoginResponse jsonResponse = JsonConvert.DeserializeObject<LoginResponse>(content);
jsonResponse.loginSuccesfull = true;
return jsonResponse;
}
else
{
return new LoginResponse() { loginSuccesfull = false };
}
}
In the project for the ios version, on a button event I use this code to run the task shown above.
Core.Service.AtmisService service = new Core.Service.AtmisService();
LoginResponse loginTask = service.Login(userName.Text, password.Text).Result;
I thought that by setting it up this way when I use .Result at the end of call to the task it would execute this task and return the result. I have added breakpoints and it enters the login method and gets as far as this var response = await client.GetAsync(uri); and then it simply does nothing. Any ideas on what I am doing wrong. Thank you for any help.
Referencing this article Async/Await - Best Practices in Asynchronous Programming
“Async all the way” means that you shouldn’t mix synchronous and
asynchronous code without carefully considering the consequences. In
particular, it’s usually a bad idea to block on async code by calling
Task.Wait or Task.Result. This is an especially common problem for
programmers who are “dipping their toes” into asynchronous
programming, converting just a small part of their application and
wrapping it in a synchronous API so the rest of the application is
isolated from the changes. Unfortunately, they run into problems with
deadlocks. After answering many async-related questions on the MSDN
forums, Stack Overflow and e-mail, I can say this is by far the
most-asked question by async newcomers once they learn the basics:
“Why does my partially async code deadlock?”
Don’t mix blocking and async code. You should go Async all the way. Your deadlock is because the .Result is blocking.
you want to do...
LoginResponse loginTask = await service.Login(userName.Text, password.Text);
To summarize this second guideline, you should avoid mixing async and
blocking code. Mixed async and blocking code can cause deadlocks,
more-complex error handling and unexpected blocking of context
threads. The exception to this guideline is the Main method for
console applications, or—if you’re an advanced user—managing a
partially asynchronous codebase.
In Your case using a button EventHandler you would do something like this:
private async void myButtonItem_Clicked(object sender, EventArgs e)
{
Core.Service.AtmisService service = new Core.Service.AtmisService();
LoginResponse loginTask = await service.Login(userName.Text, password.Text).ConfigureAwait(false);
//The rest of the code in this method may run in a different thread than the one that invoked the method. If you have ui code after this then avoid using ConfigureAwait(false)
}
You can find more information on avoiding deadlocks here.

Does HttpClient spin a new thread even if I immediately get the Result of the returned task?

In my application I like to try and be consistant and use HttpClient whenever I can. However, sometimes I dont need the asynchronous properties of HttpClient and so I simply get the Result of the Task as soon as it is returned as demonstrated in the code below.
public HttpResponseMessage httpPostWrapperMethod(string postBody, string url)
{
HttpContent content = new StringContent(postBody, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
return client.PostAsync(url, content).Result;
}
My two part question is this:
Does this code cause a new thread to be spun in the background when making the call?
and
If my calls to this other service are taking around 500ms, is this going to cause me to eat up too many threads when the service is under a production load of around 100 requests/second?
PostAsync doesn't result in a thread being created, no. In fact, were the code to be used asynchronously no thread would ever need to be doing anything for this work to be done. In your case here you're having the current thread sit around doing nothing while you wait for the operation to finish, so that is the only thread who's time is being consumed/wasted when performing this operation.

Async call with await in HttpClient never returns

I have a call I am making from inside a xaml-based, C# metro application on the Win8 CP; this call simply hits a web service and returns JSON data.
HttpMessageHandler handler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");
var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);
It hangs at the await but the http call actually returns almost immediately (confirmed through fiddler); it is as if the await is ignored and it just hangs there.
Before you ask - YES - the Private Network capability is turned on.
Any ideas why this would hang?
Check out this answer to my question which seems to be very similar.
Something to try: call ConfigureAwait(false) on the Task returned by GetStreamAsync(). E.g.
var result = await httpClient.GetStreamAsync("weeklyplan")
.ConfigureAwait(continueOnCapturedContext:false);
Whether or not this is useful depends on how your code above is being called - in my case calling the async method using Task.GetAwaiter().GetResult() caused the code to hang.
This is because GetResult() blocks the current thread until the Task completes. When the task does complete it attempts to re-enter the thread context in which it was started but cannot because there is already a thread in that context, which is blocked by the call to GetResult()... deadlock!
This MSDN post goes into a bit of detail on how .NET synchronizes parallel threads - and the answer given to my own question gives some best practices.
Just a heads up - if you miss the await at the top level in an ASP.NET controller, and you return the task instead of the result as a response, it actually just hangs in the nested await call(s) with no errors. A silly mistake, but had I seen this post it might have saved me some time checking through the code for something odd.
Disclaimer: I don't like the ConfigureAwait() solution because I find it non-intuitive and hard to remember. Instead I came to the conclusion to wrap non awaited method calls in Task.Run(() => myAsyncMethodNotUsingAwait()). This seems to work 100% but might just be a race condition!? I'm not so sure what is going on to be honest. This conclusion might be wrong and I risk my StackOverflow points here to hopefully learn from the comments :-P. Please do read them!
I just had the problem as described and found more info here.
The statement is: "you can't call an asynchronous method"
await asyncmethod2()
from a method that blocks
myAsyncMethod().Result
In my case I couldn't change the calling method and it wasn't async. But I didn't actually care about the result. As I remember it also didn't work removing the .Result and have the await missing.
So I did this:
public void Configure()
{
var data = "my data";
Task.Run(() => NotifyApi(data));
}
private async Task NotifyApi(bool data)
{
var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
await client.PostAsync("http://...", data);
}
In my case I didn't care about the result in the calling non-async method but I guess that is quite common in this use case. You can use the result in the calling async method.

Categories

Resources