What happens when HttpClient usages are not awaited - c#

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);
}

Related

GetContextAsync() with Cancellation Support

So I'm spinning up a HttpListener to wait for an OAuth2 response. In an ideal world, this is only going to be alive for a few seconds while the user logs in in the browser and we get posted the token.
I'd also like for this to have a CancellationToken so that the user can stop listening after a delay should they so wish.
My initial idea was to use something along the lines of:
_listener.Start();
Task<HttpListenerContext> t = _listener.GetContextAsync();
while (!cancelled.IsCancellationRequested)
{
if (t.IsCompleted)
{
break;
}
await Task.Run(() => Thread.Sleep(100));
}
HttpListenerContext ctx = t.Result;
//...
_listener.Stop();
But that doesn't sit right with me for so many reasons (weird async usage, polling, etc.).
So then I thought I might be able to use the synchronous version _listener.GetContext() in conjunction with Task.Run(func<T>, CancellationToken):
_listener.Start()
HttpListenerContext ctx = await Task.Run(() => _listener.GetContext(), cancelled);
//...
_listener.Stop();
This is a little better, the code's at least tidier, although it seems hacky using a synchronous version of the method asynchronously with a Task...
However this doesn't behave how I'd expect (aborting the running task when the token is cancelled).
This strikes me as something that really ought to be fairly simple to do so I assume I'm missing something.
So my question is thus... How do I listen asynchronously with a HttpListener in a cancellable fashion?
Because the GetContextAsync method does not support cancellation, it basically means that it is unlikely you can cancel the actual IO operation, yet unlikely to cancel the Task returned by the method, until you Abort or Stop the HttpListener. So the main focus here is always a hack that returns the control flow to your code.
While both the answers from #guru-stron and #peter-csala should do the trick, I just wanted to share another way without having to use Task.WhenAny.
You could wrap the task with a TaskCompletionSource like this:
public static class TaskExtensions
{
public static Task<T> AsCancellable<T>(this Task<T> task, CancellationToken token)
{
if (!token.CanBeCanceled)
{
return task;
}
var tcs = new TaskCompletionSource<T>();
// This cancels the returned task:
// 1. If the token has been canceled, it cancels the TCS straightaway
// 2. Otherwise, it attempts to cancel the TCS whenever
// the token indicates cancelled
token.Register(() => tcs.TrySetCanceled(token),
useSynchronizationContext: false);
task.ContinueWith(t =>
{
// Complete the TCS per task status
// If the TCS has been cancelled, this continuation does nothing
if (task.IsCanceled)
{
tcs.TrySetCanceled();
}
else if (task.IsFaulted)
{
tcs.TrySetException(t.Exception);
}
else
{
tcs.TrySetResult(t.Result);
}
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
return tcs.Task;
}
}
And flow the control like this:
var cts = new CancellationTokenSource();
cts.CancelAfter(3000);
try
{
var context = await listener.GetContextAsync().AsCancellable(cts.Token);
}
catch (TaskCanceledException)
{
// ...
}
I would suggest creating cancelable infinite task (Task.Delay(Timeout.Infinite, token) for example) and use Task.WhenAny. Something like that:
var cts = new CancellationTokenSource(); // token source controled by consumer "outside"
var token = cts.Token;
var httpListener = new HttpListener();
httpListener.Start();
var t = httpListener.GetContextAsync();
// to cancel the infinite delay task if listener finishes first
var localCts = CancellationTokenSource.CreateLinkedTokenSource(token);
var completed = await Task.WhenAny(t, Task.Delay(Timeout.Infinite, localCts.Token));
if (completed == t) // check that completed task is one from listener
{
localCts.Cancel(); // cancel the infinite task
HttpListenerContext ctx = t.Result;
//...
}
httpListener.Stop();
Here is yet another solution:
var cancellationSignal = new TaskCompletionSource<object>();
var contextTask = _listener.GetContextAsync();
using (cancelled.Register(state => ((TaskCompletionSource<object>)state).TrySetResult(null), cancellationSignal))
{
if (contextTask != await Task.WhenAny(contextTask, cancellationSignal.Task).ConfigureAwait(false))
break; //task is cancelled
}
Because we can't await the CancellationToken that's why have to apply the following trick
The CancellationToken does expose a Register method, where we can define a callback which will be called whenever the cancellation occurs
Here we can provide a delegate which sets an awaitable to completed
So, we can await that task
In order to create a Task which is set to completed whenever the cancellation occurs I've used TaskCompletionSource. You could also use SemaphoreSlim or any other signalling object which has async wait, like AsyncManualResetEvent.
So, we pass the cancellationSignal to the Register as a state parameter
Inside the delegate we have to cast it back to TCS to be able to call the TrySetResult on it
Inside the using block we await a Task.WhenAny
It will return that Task which finishes first
If that Task is the cancellation then we can break / return / throw ...
If that Task is the contextTask then we can continue the normal flow

HttpClientt.SendAsync doesnt wait/lock execution

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

Make the code asynchronous in C#

I have 2 methods: the first sends HTTP GET request on one address and the second calls it multiple times (so, it sends request to many IPs). Both methods are async, so they don't block code execution while the requests are proccessing remotely. The problem is, due to my poor C# knowledge, I don't know how to send all the requests simultaneously, not one after another (which my code does). That's my code:
public static async Task<string> SendRequest(Uri uri)
{
using (var client = new HttpClient())
{
var resp = await client.GetStringAsync(uri).ConfigureAwait(false);
return resp;
}
}
public static async Task<string[]> SendToAllIps(string req)
{
string[] resp = new string[_allIps.Length];
for (int i = 0; i < _allIps.Length; i++)
{
resp[i] = await SendRequest(new Uri(_allIps[i] + req));
}
return resp;
}
How to make SendToAllIps send requests without awaiting for previous task result? Also SendToAllIps must return an array of responses when all the requests are finished. As far as I understand, this can be done with Task.WaitAll, but how to use it in this particular situation?
You can use Task.WhenAll to await a collection of tasks:
public static async Task<string[]> SendToAllIps(string req)
{
var tasks = _allIps.Select(ip => SendRequest(new Uri(ip + req)));
return await Task.WhenAll(tasks);
}
Answers provided above provide the correct way of doing it but doesn't provide rationale, let me explain what's wrong with your code:
Following line creates an issue:
resp[i] = await SendRequest(new Uri(_allIps[i] + req));
Why ?
As you are awaiting each individual request, it will stop the processing of the remaining requests, that's the behavior of the async-await and it will be almost the synchronous processing of each SendRequest when you wanted then to be concurrent.
Resolution:
SendRequest being an async method returns as Task<string>, you need add that to an IEnumerable<Task<string>> and then you have option:
Task.WaitAll or Task.WhenAll
Yours is Web API (Rest Application), which needs a Synchronization Context, so you need Task.WhenAll, which provides a Task as result to wait upon and integrate all the task results in an array, if you try using Task.WaitAll it will lead to deadlock, as it is not able to search the Synchronization Context, check the following:
WaitAll vs WhenAll
You can use the Task.WaitAll only for the Console application not the Web Application, Console App doesn't need any Synchronization Context or UI Thread
public static async Task<string[]> SendToAllIps(string req)
{
var tasks = new List<Task<string>>();
for (int i = 0; i < _allIps.Length; i++)
{
// Start task and assign the task itself to a collection.
var task = SendRequest(new Uri(_allIps[i] + req));
tasks.Add(task);
}
// await all the tasks.
string[] resp = await Task.WhenAll(tasks);
return resp;
}
The key here is to collect all the tasks in a collection and then await them all using await Task.WhenAll. Even though I think the solution from Lee is more elegant...

Async/Await in foreach with HTTPClient

I have a webservice that loads up some plugins (dlls) and calls their Process method. One of the plugins takes a list of members and ensures that they are all included in a MailChimp list.
Here is the code that adds the users to the MailChimp group.
private async Task AddMCUsers(List<Member> _memberList)
{
using (var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user:password");
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", creds);
string memberURI = string.Format(#"{0}lists/{1}/members", _baseURI, _memberGroupId);
var jss = new JavaScriptSerializer();
foreach (var user in _memberlist)
{
var _addStatus = "";
try
{
var content = jss.Serialize(new MCPost()
{
email_address = user.Email,
status = "subscribed",
merge_fields = new MCMergeFields()
{
FNAME = user.Firstname,
LNAME = user.Lastname
}
});
using(var result = await http.PostAsync(memberURI, new StringContent(content,Encoding.UTF8, "application/json")))
{
var resultText = await result.Content.ReadAsStringAsync();
if(result.IsSuccessStatusCode)
{
_addStatus = "Success";
var _returnedUser = jss.Deserialize<MCMember>(resultText);
//Store new user's id
user.ServiceId = _returnedUser.id;
}
else
{
_addStatus = "Fail";
}
}
}
catch {
_addStatus = "Error";
}
LogEvent("Add User - " + _addStatus, string.Format("Id: {0} - {1} {2} (Account: {3}) : {4}", user.Id, user.Firstname, user.Lastname, user.AccountId, user.Email));
}
}
}
In normal procedural code, this wouldn't be a problem. However, the only Post method available on the httpClient was PostAsync. Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
I'm not sure what happens with await when its wrapped in a foreach like I have. Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
My other question is, what is actually going to be returned. IOW, my understanding is that await returns a Task. However, here, I'm looping through the list and making multiple calls to await PostAsync. My method returns a Task. But which task gets returned? If my calling method needs to wait for completion before moving on, what does its call look like?
private void Process()
{
//Get List
var task = AddMCUsers(list);
task.Wait();
//Subsequent processing
}
I've read that you should use Async all the way. Does this mean my calling method should look more like this?
public async Task Process()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
Thanks to whatever help you can offer on this.
In normal procedural code, this wouldn't be a problem.
The whole point of async/await is to write asynchronous code in a way that looks practically identical to "normal" synchronous code.
Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
HttpClient was intended to be reused; in fact, it can be used for any number of calls simultaneously.
I'm not sure what happens with await when its wrapped in a foreach like I have.
One way to think of it is that await "pauses" the method until its operation completes. When the operation completes, then the method continues executing. I have an async intro that goes into more detail.
Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
No, that's fine.
IOW, my understanding is that await returns a Task.
await takes a Task. It "unwraps" that task and returns the result of the task (if any). If the task completed with an exception, then await raises that exception.
My method returns a Task. But which task gets returned?
The Task returned from an async method is created by the async state machine. You don't have to worry about it. See my intro for more details.
If my calling method needs to wait for completion before moving on, what does its call look like? ... I've read that you should use Async all the way. Does this mean my calling method should look more like this?
Yes, it should look like your second snippet:
public async Task ProcessAsync()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
The only thing I changed was the Async suffix, which is recommended by the Task-based Asynchronous Pattern.
in your code you should be fine with reusing the HttpClient. What async / await is allow the code to release the execution thread to prevent locking a cpu thread while waiting for the web response. It also releases control back to the caller. When releasing code back to the caller it means that if your Process function does not await your AddMCUsers, Process could finish before AddMCUsers (useful in fire and forget situations to not await a method).
What async/await do not do is affect the logical flow of an individual method. When you await an async web call the execution is paused and then resumed at the same point once the web call returns. There is also thread context tracking and the code resumes in the same context (ie. UI thread or background thread depending on the parent) by default, but this can be changed if needed.
At some point in your code you may want to have a method that blocks until your async code competes and that is where you will want your Task.Wait() call to block execution. If all you use is awaits then it is possible for your program to end before your task competes. See the code example below.
class Program
{
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
}
static async Task waitAsync()
{
await Task.Delay(5000);
}
}
in the sample with out a Task.Wait call to block the Main method the program will end before the 5 second wait is complete. Having a main method of the following will cause the program to wait for 5 seconds before exiting:
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
waitForMe.Wait();
}

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()

Categories

Resources