Edit: This question looks like it might be the same problem, but has no responses...
Edit: In test case 5 the task appears to be stuck in WaitingForActivation
state.
I've encountered some odd behaviour using the System.Net.Http.HttpClient in .NET 4.5 - where "awaiting" the result of a call to (e.g.) httpClient.GetAsync(...)
will never return.
This only occurs in certain circumstances when using the new async/await language functionality and Tasks API - the code always seems to work when using only continuations.
Here's some code which reproduces the problem - drop this into a new "MVC 4 WebApi project" in Visual Studio 11 to expose the following GET endpoints:
/api/test1
/api/test2
/api/test3
/api/test4
/api/test5 <--- never completes
/api/test6
Each of the endpoints here return the same data (the response headers from stackoverflow.com) except for /api/test5
which never completes.
Code to reproduce:
public class BaseApiController : ApiController
{
/// <summary>
/// Retrieves data using continuations
/// </summary>
protected Task<string> Continuations_GetSomeDataAsync()
{
var httpClient = new HttpClient();
var t = httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);
return t.ContinueWith(t1 => t1.Result.Content.Headers.ToString());
}
/// <summary>
/// Retrieves data using async/await
/// </summary>
protected async Task<string> AsyncAwait_GetSomeDataAsync()
{
var httpClient = new HttpClient();
var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);
return result.Content.Headers.ToString();
}
}
public class Test1Controller : BaseApiController
{
/// <summary>
/// Handles task using Async/Await
/// </summary>
public async Task<string> Get()
{
var data = await Continuations_GetSomeDataAsync();
return data;
}
}
public class Test2Controller : BaseApiController
{
/// <summary>
/// Handles task by blocking the thread until the task completes
/// </summary>
public string Get()
{
var task = Continuations_GetSomeDataAsync();
var data = task.GetAwaiter().GetResult();
return data;
}
}
public class Test3Controller : BaseApiController
{
/// <summary>
/// Passes the task back to the controller host
/// </summary>
public Task<string> Get()
{
return Continuations_GetSomeDataAsync();
}
}
public class Test4Controller : BaseApiController
{
/// <summary>
/// Handles task using Async/Await
/// </summary>
public async Task<string> Get()
{
var data = await AsyncAwait_GetSomeDataAsync();
return data;
}
}
public class Test5Controller : BaseApiController
{
/// <summary>
/// Handles task by blocking the thread until the task completes
/// </summary>
public string Get()
{
var task = AsyncAwait_GetSomeDataAsync();
var data = task.GetAwaiter().GetResult();
return data;
}
}
public class Test6Controller : BaseApiController
{
/// <summary>
/// Passes the task back to the controller host
/// </summary>
public Task<string> Get()
{
return AsyncAwait_GetSomeDataAsync();
}
}
This question is related to
c#
.net
asynchronous
async-await
dotnet-httpclient
Edit: Generally try to avoid doing the below except as a last ditch effort to avoid deadlocks. Read the first comment from Stephen Cleary.
Quick fix from here. Instead of writing:
Task tsk = AsyncOperation();
tsk.Wait();
Try:
Task.Run(() => AsyncOperation()).Wait();
Or if you need a result:
var result = Task.Run(() => AsyncOperation()).Result;
From the source (edited to match the above example):
AsyncOperation will now be invoked on the ThreadPool, where there won’t be a SynchronizationContext, and the continuations used inside of AsyncOperation won’t be forced back to the invoking thread.
For me this looks like a useable option since I do not have the option of making it async all the way (which I would prefer).
From the source:
Ensure that the await in the FooAsync method doesn’t find a context to marshal back to. The simplest way to do that is to invoke the asynchronous work from the ThreadPool, such as by wrapping the invocation in a Task.Run, e.g.
int Sync() { return Task.Run(() => Library.FooAsync()).Result; }
FooAsync will now be invoked on the ThreadPool, where there won’t be a SynchronizationContext, and the continuations used inside of FooAsync won’t be forced back to the thread that’s invoking Sync().
I'm going to put this in here more for completeness than direct relevance to the OP. I spent nearly a day debugging an HttpClient
request, wondering why I was never getting back a response.
Finally found that I had forgotten to await
the async
call further down the call stack.
Feels about as good as missing a semicolon.
I was using to many await, so i was not getting response , i converted in to sync call its started working
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(URL);
var response = client.GetAsync(URL).Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
In my case 'await' never finished because of exception while executing the request, e.g. server not responding, etc. Surround it with try..catch to identify what happened, it'll also complete your 'await' gracefully.
public async Task<Stuff> GetStuff(string id)
{
string path = $"/api/v2/stuff/{id}";
try
{
HttpResponseMessage response = await client.GetAsync(path);
if (response.StatusCode == HttpStatusCode.OK)
{
string json = await response.Content.ReadAsStringAsync();
return JsonUtility.FromJson<Stuff>(json);
}
else
{
Debug.LogError($"Could not retrieve stuff {id}");
}
}
catch (Exception exception)
{
Debug.LogError($"Exception when retrieving stuff {exception}");
}
return null;
}
These two schools are not really excluding.
Here is the scenario where you simply have to use
Task.Run(() => AsyncOperation()).Wait();
or something like
AsyncContext.Run(AsyncOperation);
I have a MVC action that is under database transaction attribute. The idea was (probably) to roll back everything done in the action if something goes wrong. This does not allow context switching, otherwise transaction rollback or commit is going to fail itself.
The library I need is async as it is expected to run async.
The only option. Run it as a normal sync call.
I am just saying to each its own.
Since you are using .Result
or .Wait
or await
this will end up causing a deadlock in your code.
you can use ConfigureAwait(false)
in async
methods for preventing deadlock
like this:
var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false);
you can use
ConfigureAwait(false)
wherever possible for Don't Block Async Code .
Source: Stackoverflow.com