How to handle async methods properly in .NET Core - c#

I am looking to retrieve a website response and send dots to the screen whilst doing so.
I am unable to do so using the code below. I'm sure I'm doing something rather silly, looking for any insights..
Console.Write("Doing Task...");
HttpClient web = new HttpClient();
Task<HttpResponseMessage> resp = web.GetAsync("http://localhost",
HttpCompletionOption.ResponseContentRead);
resp.RunSynchronously(); //Doesn't Work
resp.Start(); //Doesn't Work
while (resp.Status != TaskStatus.RanToCompletion)
{
Write.Operation(".");
Thread.Sleep(500);
}
Console.Write("Done!");

Why don't you use async/await? Starting from C# 7.1 main method can be declared as async.
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Console.WriteLine("Doing Task...");
using HttpClient client = new HttpClient();
var resp = await client.GetAsync("http://localhost");
var content = await resp.Content.ReadAsStringAsync();
Console.WriteLine("Done!");
}
}

you can use async/await like the example below to capture any data returned from server in .NET Core:
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
var result = await client.GetAsync("http://localhost");
Console.WriteLine(result.StatusCode);
}

The problem with your code is that you are waiting for the successful execution of the Task, and this may never happen. You need to wait for its completion instead, whether successful or unsuccessful:
Console.Write("Doing Task...");
HttpClient httpClient = new HttpClient();
var task = httpClient.GetAsync("http://localhost");
while (!task.IsCompleted)
{
Console.Write(".");
Thread.Sleep(500);
}
task.Wait(); // observe any exceptions that may have occured
Console.WriteLine("Done!");
Alternatively you could use the Wait overload that accepts a timeout. This is actually better because the completion of the task will be observed immediately, and not with a random 0-500 msec delay.
while (!task.Wait(500))
{
Console.Write(".");
}
Console.WriteLine("Done!");

Related

Why is SemaphoreSlim not releasing in async Task?

I need to call an API thousands of times as quickly as possible. The API has a limit of 10 calls per second. In order to take full advantage of the 10 calls per second without going over, I'm calling the API asynchronously and throttling the calls with a semaphore and a timer. My code enters the semaphore, calls the API, and then makes sure at least one second has passed before it releases the semaphore.
The API call is actually pretty quick and returns in about a second or less, so my code should move right on to the check time/release semaphore logic. However, what actually happens is after 10 calls the semaphore fills up and is not released at all until the rest of the async Tasks to call the API are created. After that everything works as expected, so I'm not experiencing any real issues. The behavior just seems strange and I would like to understand it.
public static class MyClass
{
SemaphoreSlim semaphore = new SemaphoreSlim(10);
public static async Task CreateCallApiTasks(IList<object> requests)
{
var callApiTasks = requests.Select(x => CallApi(x));
await Task.WhenAll(callApiTasks);
}
private static async Task CallApi(object requestBody)
{
using (var request = new HttpRequestMessage(HttpMethod.Post, <apiUri>))
{
request.Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
using (var httpClient = new HttpClient())
{
var throttle = new Stopwatch();
ExceptionDispatchInfo capturedException = null;
await semaphore.WaitAsync();
try
{
throttle.Start();
response = await httpClient.SendAsync(request);
while (throttle.ElapsedMilliseconds < 1000)
{
await Task.Delay(1);
}
semaphore.Release();
throttle.Stop();
}
catch (Exception ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
while (throttle.ElapsedMilliseconds < 1000)
{
await Task.Delay(1);
}
semaphore.Release();
throttle.Stop();
capturedException.Throw();
}
}
}
}
}

Trying to find why my PostAsync(URL,BODY).Results will Time out, but only in .NET 4.6

I'm trying to Create a Docker Container via a POST Request to a REST API.
In .NET Core 2.0 The following Code works
using System;
using System.Net.Http;
using System.Text;
namespace DockerDebug
{
class Program
{
static void Main(string[] args)
{
string reply = string.Empty;
try
{
using (HttpClient dockerClient = new HttpClient())
{
StringContent httpContent = new StringContent("{\"Image\":\"busybox\"}", Encoding.UTF8, "application/json");
HttpResponseMessage dockerRepsponse = dockerClient .PostAsync("http://localhost:2375/v1.29/containers/create?name=bussybox", httpContent).Result;
reply = dockerRepsponse.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
reply = ex.Message;
}
Console.WriteLine(reply);
}
}
}
My Docker Environment Does have Expose daemon on tcp://localhost:2357 without TLS turned on.
I can run the above command vis a REST Call with Chrome's REST API Plug in, or POSTMAN.
Also the above code works in .NET Core 2.0
but when Building .NET 4.5 & 4.6.1 it times out every time on the PostAsync call.
The first thing I would do is to remove the .Result you are doing everywhere to forcefully drop async Tasks back to synchronous returns. It is bad and could introduce locks as well as other issues.
Instead a simple fix to convert it to true async and still allow the console to wait is like this:
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var tcs = new TaskCompletionSource<bool>();
string reply = string.Empty;
Task.Run(async () =>
{
try
{
using (var httpClient = new HttpClient())
{
var httpContent = new StringContent("{\"Image\":\"busybox\"}", Encoding.UTF8, "application/json");
var result = await httpClient.PostAsync("http://localhost:2375/v1.29/containers/create?name=bussybox", httpContent);
reply = await result.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
reply = ex.Message;
tcs.TrySetResult(false);
}
});
tcs.Task.Wait();
Console.WriteLine(reply);
}
}
}
Basically create a TaskCompletionSource<bool> and wait for that instaed. Then run a new Task which will start a background thread and run off the main thread.
Once your task is complete set the tcs so that the Wait() call continues.
Here is my video on Threads to help you understand at least Threads, and I will cover Tasks shortly but it should give you some insight into the Task.Run near the very end of the video. https://www.youtube.com/watch?v=XXg9g56FS0k
See if that fixes your issue. If not it's likely the simple fact the HttpClient is implemented differently in .Net Framework than it is in .Net Core. So for that you can change the Timeout to much longer if you like with:
httpClient.Timeout = TimeSpan.FromMinutes(10);
To set a timeout of 10 minutes.
Between those two things I don't think you should have an issue.

Call HttpClient.GetAsync in console application - Deadlock

I searched and saw many posts and I don't know why this simple console call using httpclient.getasync and awaiting it causes it not to complete. here is the code:
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static void Main(string[] args)
{
GetAPI().Wait();
Console.WriteLine("Hello");
//Console.ReadLine();
}
public static async Task GetAPI()
{
using (HttpClient client = new HttpClient())
{
var response = await client.GetAsync("https://www.google.com/");
//var response = client.GetAsync("https://www.google.com/").Result
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
}
If I change to use client.GetAsync("https://www.google.com/").Result; and remove "await" (see commented out code) it will work however I believe that is a no no because I am making an asynchronous function into synchronous. I've seen other posts and blogs about this and it all claims what I am trying to do is correct but when running the example app it just doesn't turn out that way.
You can call wait() in Main.
But be carefull, this only works in console applications and will dead lock in UI applications like WPF, WinForms or in ASP.NET context...
public static void Main(string[] args)
{
try
{
var t = GetAPI();
t.Wait();
Console.WriteLine(t.Result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static async Task<string> GetAPI()
{
using (HttpClient client = new HttpClient())
{
var response = await client.GetAsync("https://www.google.com/");
string content = await response.Content.ReadAsStringAsync();
return content;
}
}
MSDN
This code will work just fine in a console application but will
deadlock when called from a GUI or ASP.NET context.
The issue is that awaiting the client.GetAsync, after getting the result is waiting for main thread to be free and main thread is waiting for client.GetAsync to complete.
var response = await client.GetAsync("https://www.google.com/").ConfigureAwait(false);
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
Will tell that client.GetAsync to complete on other thread and then return the result back to main thread so that no more dead lock.

Why does "RunAsync().Wait()" never finishes running, but if i take off the ".Wait()" it returns me the right result?

I have the following code:
public MainWindow()
{
InitializeComponent();
RunAsync().Wait();
}
private static async Task RunAsync()
{
var baseAddress = new Uri("https://api.trakt.tv/");
using (var httpClient = new HttpClient { BaseAddress = baseAddress })
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("trakt-api-version", "2");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("trakt-api-key", "");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var response = await httpClient.GetAsync("search?query=Arrow&type=Show"))
{
string responseData = await response.Content.ReadAsStringAsync();
}
}
}
When I run it, it get stuck at the part:
using (var response = await httpClient.GetAsync("search?query=Arrow&type=Show"))
This never finishes running. But if I take off the .Wait() from RunAsync().Wait(); then the code run to the end normally. Why is it getting stuck when I put .Wait() on the method call?
I have to put it, because if i don't the rest of the code will continue to run and wont wait the method to complete...
You've got a deadlock here.
Code after await will be executed in the same thread as it started. This is the way await works. But since you've already blocked the thread by calling Wait(), it goes to be locked forever

How to create HttpWebRequest without interrupting async/await?

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.

Categories