I have a bunch of slow functions that are essentially this:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
This works nicely and asynchronously except for the call to WebRequest.Create - this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.
I already have this code written using BackgroundWorkers, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?
I've seen this nice answer about asyncifying a WebRequest, but even there the object itself is created synchronously.
Interestingly, I'm not seeing a blocking delay with WebRequest.Create or HttpClient.PostAsync. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.
Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:
private async Task<List<string>> DownloadSomething()
{
var request = await Task.Run(() => {
// WebRequest.Create freezes??
return System.Net.WebRequest.Create("https://valid.url");
});
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a WebRequest instance to be able to call Abort on it.
Using HttpClient, cancellation would be possible, something like this:
private async Task<List<string>> DownloadSomething(CancellationToken token)
{
var httpClient = new HttpClient();
var response = await Task.Run(async () => {
return await httpClient.PostAsync("https://valid.url", token);
}, token);
// ...
}
With HttpClient, you can also register a httpClient.CancelPendingRequests() callback on the cancellation token, like this.
[UPDATE] Based on the comments: in your original case (before introducing Task.Run) you probably did not need the IProgress<I> pattern. As long as DownloadSomething() was called on the UI thread, every execution step after each await inside DownloadSomething would be resumed on the same UI thread, so you could just update the UI directly in between awaits.
Now, to run the whole DownloadSomething() via Task.Run on a pool thread, you would have to pass an instance of IProgress<I> into it, e.g.:
private async Task<List<string>> DownloadSomething(
string url,
IProgress<int> progress,
CancellationToken token)
{
var request = System.Net.WebRequest.Create(url);
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
// read stream and return data
progress.Report(...); // report progress
}
}
// ...
// Calling DownloadSomething from the UI thread via Task.Run:
var progressIndicator = new Progress<int>(ReportProgress);
var cts = new CancellationTokenSource(30000); // cancel in 30s (optional)
var url = "https://valid.url";
var result = await Task.Run(() =>
DownloadSomething(url, progressIndicator, cts.Token), cts.Token);
// the "result" type is deduced to "List<string>" by the compiler
Note, because DownloadSomething is an async method itself, it is now run as a nested task, which Task.Run transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.
Also check out: Enabling Progress and Cancellation in Async APIs.
I think you need to use HttpClient.GetAsync() which returns a task from an HTTP request.
http://msdn.microsoft.com/en-us/library/hh158912(v=vs.110).aspx
It may depend a bit on what you want to return, but the HttpClient has a whole bunch of async methods for requests.
Related
I have a ASP.NET controller that internally calls an API and cache for answers and returns the answer from the first task completed. In general, API is much slower than cache and cache returns in 50ms while API returns in 2s at the 95th percentile.
Requirements:
Return answer from cache immediately if available. Otherwise wait for API and return available/empty response as the case maybe.
Once API call completes, we update the cache with the new answer if available.
Problem:
How to await API call and write to cache in a background thread without blocking controller?
Current flow:
Task<string>[] getDataTasks = new Task<string>[]
{
getDataFromAPI(),
getDataFromCache()
};
var finishedTaskIndex = Task.WaitAny(getDataTasks);
var response = getDataTasks[finishedTaskIndex].Result;
if (finishedTaskIndex == 1 && string.IsNullOrEmpty(response))
{
response = await getDataTasks[0];
writeToCacheTask(response); //fire and forget, this is non-blocking
}
**//Problem :- How to await for response from API and write to cache in a non-blocking manner** THIS DOES NOT WORK
Task.Run(async () =>
{
var apiResponse = await getDataTasks[0];
writeToCacheTask(apiResponse);
});
return response;
In the above, even though I am using a new thread to await the api call independent of the main thread, it does not work.
Does not work
Tried using ContinueWith on the Task with a callback with TaskContinuationOptions = ExecuteSynchronously as this continues on the same thread the task was invoked on. But I am perhaps understanding it wrongly.
Task.Run(async () =>
{
return await getDataTasks[0];
}).ContinueWith(this.CallBack, TaskContinuationOptions.ExecuteSynchronously);
Works: Using delegate handlers
// private members of controller
private delegate string DelegateHandler();
private string getDataFromAPISync()
{
return getDataFromAPI().GetAwaiter().GetResults();
}
private string getDataFromCacheSync()
{
return getDataFromCache().GetAwaiter().GetResults();
}
private void CallBack(IAsyncResult ar)
{
var apiResponse = apiInvoker.EndInvoke(ar);
writeToCacheTask(apiResponse);
}
// In controller
var apiInvoker = new DelegateHandler(this.getDataFromAPISync)
var cacheInvoker = new DelegateHandler(this.getDataFromCacheSync)
IAsyncResult apiResults = apiInvoker.BeginInvoke(this.CallBack, null);
IAsyncResult cacheResults = cacheInvoker.BeginInvoke(null, null);
var handles = new WaitHandle[]
{
apiResults.AsyncWaitHandle,
cacheResults.AsyncWaitHandle
}
WaitHandle.WaitAny(handles);
if (cacheResults.IsCompleted)
{
return cacheInvoker.EndInvoke(ar);
}
return apiInvoker.EndInvoke(ar);
When I am using delegate handler, it looks like the background thread used to make the api call, is used to handle the callback as well and only once callback is completed it is killed.
How to do the same using the Task library?
I am trying to call a service, but the service has a max length per request, so I am splitting my request into multiple smaller requests.
I am then trying to to use HttpClient together with Await as its async
public async Task<string> CallGenoskanAsync(List<string> requestList)
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var credentials = new NetworkCredential(userId, password);
var tasks = new List<Task<string>>();
foreach (string requestString in requestList)
{
using (HttpClientHandler handler = new HttpClientHandler { Credentials = credentials })
{
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(baseAddress);
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
using (HttpContent content = response.Content)
{
tasks.Add(content.ReadAsStringAsync());
}
}
}
}
Task.WaitAll(tasks.ToArray());
var result = "";
foreach (var task in tasks)
{
result += task.Result;
}
return result;
}
This code deadlocks when await client.GetAsync is called, it never finishes.
If I change that line to
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
Then I dont get any deadlocks, I suppose I am using await improperly together with foreach, but I can not figure out how
Edit: Changed example code
The compiler will give you a warning for the code you posted; in particular, it will point out that CallGenoskanAsync is synchronous, not asynchronous.
The core problem is this line:
Task.WaitAll(tasks.ToArray());
When you're writing asynchronous code, you shouldn't block on it. To do so can cause deadlocks, as I explain in my blog post. The deadlock occurs because await will capture a "context" that it uses to resume execution of the async method. This "context" is SynchronizationContext.Current or TaskScheduler.Current, and many contexts (UI and ASP.NET request contexts in particular) only allow one thread. So, when your code blocks a thread (Task.WaitAll), it's blocking a thread in that context, and this prevents the await from continuing since it's waiting for that context.
To fix, make your code asynchronous all the way. As I explain in my async intro post, the asynchronous equivalent of Task.WaitAll is await Task.WhenAll:
await Task.WhenAll(tasks);
WhenAll also has the nice property that it unwraps the results for you, so you don't have to use the problematic Result property:
var results = await Task.WhenAll(tasks);
return string.Join("", results);
Relevant context is not provided:
If
Your code sample is part of a callstack that returns a Task
There is a blocking wait at the root of the callstack (e.g.,
DoSomething().Result or DoSomething.Wait())
You have a SynchronizationContext that's thread affinitized (IE, this is running pretty much anywhere but a Console application)
Then
Your synchronization context's thread may be blocked on the root Result/Wait(), so it can never process the continuation that the completed call to GetAsync() scheduled to it. Deadlock.
If you meet the above conditions, try applying .ConfigureAwait(false) to your awaited GetAsync. This will let the continuation get scheduled to a Threadpool thread. Be aware that you are potentially getting scheduled to a different thread at that point.
I have a list of selected contentIds and for each content id I need to call an api, get the response and then save the received response for each content in DB.
At a time a user can select any number of content ranging from 1-1000 and can pass on this to update the content db details after getting the response from api.
In this situation I end up creating multiple requests for each content.
I thought to go ahead with asp.net async Task operation and then wrote this following method.
The code I wrote currently creates one one task for each contentId and atlast I am waiting from all task to get the response.
Task.WaitAll(allTasks);
public static Task<KeyValuePair<int, string>> GetXXXApiResponse(string url, int contentId)
{
var client = new HttpClient();
return client.GetAsync(url).ContinueWith(task =>
{
var response = task.Result;
var strTask = response.Content.ReadAsStringAsync();
strTask.Wait();
var strResponse = strTask.Result;
return new KeyValuePair<int, string>(contentId, strResponse);
});
}
I am now thinking for each task I create it will create one thread and in turn with the limited no of worker thread this approach will end up taking all the threads, which I don't want to happen.
Can any one help/guide me how to handle this situation effectively i.e handling multiple api requests or kind of batch processing using async tasks etc?
FYI: I'm using .NET framework 4.5
A task is just a representation of an asynchronous operation that can be waited on and cancelled. Creating new tasks doesn't necessarily create new threads (it mostly doesn't). If you use async-await correctly you don't even have a thread during most of the asynchronous operation.
But, making many requests concurrently can still be problematic (e.g. burdening the content server too much). So you may still want to limit the amount of concurrent calls using SemaphoreSlim or TPL Dataflow:
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(100);
public static async Task<KeyValuePair<int, string>> GetXXXApiResponse(string url, int contentId)
{
await _semaphore.WaitAsync();
try
{
var client = new HttpClient();
var response = await client.GetAsync(url);
var strResponse = await response.Content.ReadAsStringAsync();
return new KeyValuePair<int, string>(contentId, strResponse);
}
finally
{
_semaphore.Release();
}
}
To wait for these tasks to complete you should use Task.WhenAll to asynchronously wait instead of Task.WaitAll which blocks the calling thread:
await Task.WhenAll(apiResponses);
How do I convert this chain of synchronous method calls into async (using the async/await operators)? Given that only the last call, DoRequest(), is the one that takes time to execute, is that the only method that needs to become async? Or do all the callers in the chain, RequestSomething() and Process(), need to be made async as well?
[HttpGet]
void Process()
{
var url = "http://someapi.com";
var myObject= RequestSomething(url);
//do something with the myObject.
}
MyObject RequestSomething(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = DoRequest(request);
return JsonConvert.DeserializeObject<MyObject>(response);
}
//method that takes time to return.
HttpResponseMessage DoRequest(HttpRequestMessage request)
{
var client = new HttpClient();
return client.SendAsync(request).Result;
}
To do async correctly it is "infectious", if you do it in one spot you need to do it all the way up the call chain to get any of the real benefits out of it. So whatever is calling Process() will need to handle the task returned from Process by either awaiting it or passing it up the chain like DoRequest does.
async Task Process()
{
var url = "http://someapi.com";
var myObject= await RequestSomething(url);
//do something with the myObject.
}
async Task<MyObject> RequestSomething(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await DoRequest(request).ConfigureAwait(false);
return JsonConvert.DeserializeObject<MyObject>(response);
}
//method that takes time to return.
Task<HttpResponseMessage> DoRequest(HttpRequestMessage request)
{
var client = new HttpClient();
return client.SendAsync(request);
}
Because you do not do any extra work after performing the request you don't need async/await in your DoRequest function, but the other ones will need the async/await keywords. The .ConfigureAwait(false) makes it so that function does not have to run the rest of its code on the UI thread, this can give you a small performance boost. I did not know if the code that is in //do something with the myObject. required you being on the UI thread or not, so I did not put it on that await, but if you don't need to be on the UI thread you could add it there too.
You should make DoRequest...
Public async Task<HttpResponseMessage> DoRequest(...
Then return await client.SendAsync
Similarly, DoSomething should be async Task<...>
Your calling method, Process can be async void if you want to call it as fire and forget, otherwise an async Task.
Try:
var task = Task.Run(() => RequestSomething());
var task2 = Task.Run(() => RequestSomethingElse());
await Task.WhenAll(task, task2);
You can specify more than two if you like, or only one.
I'm pulling data from Amazon via HTTP. The code works just fine in a small demo project, but in my main app it doesn't. When I call FetchItem() I receive this output:
'System.Net.Http.Formatting.dll'. Cannot find or open the PDB file.
After await client.GetAsync() the function returns and url.Wait() waits forever.
Usage
Task<string> url = FetchItem("ItemName", requestUrl);
url.Wait();
return url.Result;
Source of FetchItem
private static async Task<string> FetchItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
XElement content = await response.Content.ReadAsAsync<XElement>();
XNamespace ns = NAMESPACE;
var isValidResults = content.Descendants(ns + "IsValid").AsParallel();
foreach (var item in isValidResults)
{
if (item.Value != "True")
return "Invalid Request";
}
var titleResults = content.Descendants(ns + sItemName).AsParallel();
foreach (var item in titleResults)
{
if (item.Name == ns + sItemName)
return item.Value;
// depending on the keyword, results can be quite fun.... :-)
}
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return "Error";
}
I assume you're calling this code on the UI thread. What's happening is that Waiting for the task to complete causes a deadlock. Your FetchItem method is asynchronous, and when you use await in it, the code that follows is transformed to a callback (called the continuation) that will be executed on the UI thread. But since the UI thread is busy waiting for the task to complete, it can't process the callback, so the task never completes. Hence the deadlock.
You should never Wait on the result of an async method. If you call an async method, use await to get its result. It means that the calling method also has to be async. Basically, when you start to use async in some part of the code, all the code that uses it needs to become async as well... (*)
(*) well, that's not exactly true. You can prevent the async method from resuming on the UI thread, by calling ConfigureAwait(false) on the method you await. If you do that, the continuation will run on a thread pool thread, instead of the UI thread. This will also avoid the deadlock.
I changed FetchItem() to run sync. That did it for me:
private static string GetItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
XElement content = response.Content.ReadAsAsync<XElement>().Result;
...
It doesn't make sense to me to rewrite half my app to go async.