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.
Related
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!");
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.
I'm coding a plug-in for Excel. I'd like to add a new method to excel that can crawl a web page and get back the html code.
my problem is that i have a lot of URLs to proces and if I use a sync method, it will take a lot of time and freeze my excel.
let say, i have a cell A1 which contains "http://www.google.com", and in A2, my method "=downloadHtml(A1)".
I'm using HttpClient because it is already handling Async. So here is my code :
static void Main()
{
GetWebPage(new Uri("http://www.google.com"));
}
static async void GetWebPage(Uri URI)
{
string html = await HttpGetAsync(URI);
//Do other operations with html code
Console.WriteLine(html);
}
static async Task<string> HttpGetAsync(Uri URI)
{
try
{
HttpClient hc = new HttpClient();
Task<Stream> result = hc.GetStreamAsync(URI);
Stream vs = await result;
StreamReader am = new StreamReader(vs);
return await am.ReadToEndAsync();
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
Console.WriteLine("domain_not_found");
break;
//Catch other exceptions here
}
}
return "";
}
The probem is that, when i run the program, the program exits before the task complete.
If i add a
Console.ReadLine();
the program will not exit do to the readline instruction, and after a couple of seconds, i see the html printed into my screen (du to the console.writeline instruction). So the program works.
how can i handle this ?
GetWebPage is a fire-and-forget method (an async void), so you cannot wait for it to finish.
You should be using this instead:
static void Main()
{
string html = Task.Run(() => HttpGetAsync(new Uri("http://www.google.com"))).GetAwaiter().GetResult();
//Do other operations with html code
Console.WriteLine(html);
}
Also, you could simplify the download code to this:
using (var HttpClient hc = new HttpClient())
{
return await hc.GetStringAsync(URI);
}
I have a WinForms application that has two roles. If no command line parameters are present, the Main function calls Application.Run, and presents the UI. If command line parameters are present, Application.Run is NOT called. Instead, I call an async method like this:
result = HandleCommandLine(args).GetAwaiter().GetResult();
(I am new to async/await, and this form was based on a SO answer).
The end goal is to loop through a list, and for each entry, start a new task. Each of those tasks should run in parallel with the others. The tasks are started like this:
runningTasks.Add(Task.Factory.StartNew((args) => HandlePlayback( (Dictionary<string,string>) ((object[])args)[0]), new object[] { runArgs } ));
The tasks are added to the collection of runningTasks, and I later call:
Task.WaitAll(runningTasks.ToArray());
In each of the runningTasks, I am trying to send web requests using HttpClient:
using (HttpResponseMessage response = await Client.SendAsync(message))
{
using (HttpContent responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
}
}
Once Client.SendAsync is called, the whole thing goes belly up. All of my runningTasks complete, and the application exits. Nothing past the Client.SendAsync executes in any of those tasks.
Since I am new at async/await, I have very few ideas about what exactly might be wrong, and hence few ideas about how to fix it. I imagine it has something to do with the SynchronizationContexts in this situation (WinForms app acting like a console app), but I'm not grasping what I need to do and where to keep the service request and the web request async calls from causing everything to complete too early.
I guess my question then is, why are (only some) 'awaited' calls causing all tasks to complete? What can I do about it?
UPDATE:
Two things. #Joe White: The WindowsFormsSynchronizationContext.Current is always null wherever I check.
#David Pine: Minimal (kind of :) ) complete viable example follows. You will either need to add a command line argument to the project, or force execution to the HandleCommandLine function. In this example, it tries to make a website request for each of three sites. It doesn't appear to matter if they exist. The code reaches the Client.SendAsync some number of times (usually not three), but timing appears to matter.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
static List<Task> runningTasks = new List<Task>();
[STAThread]
static int Main()
{
int result = 1; // true, optimism
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
if (args.Length > 1)
{
// do the command line work async, while keeping this thread active
result = HandleCommandLine(args).GetAwaiter().GetResult();
}
else
{
// normal interface mode
Application.Run(new Form1());
}
return result;
}
static async Task<int> HandleCommandLine(string[] args)
{
// headless mode
int result = 1; // true, optimism
result = await HandleControlMode(args);
return result;
}
private static async Task<int> HandleControlMode(string[] Arguments)
{
int result = 1; // optimism
try
{
List<string> sites = new List<string>() { #"http://localhost/site1", #"http://localhost/site2", #"http://localhost/site3" };
foreach (string site in sites)
{
Begin(site); // fire off tasks
// the HandleControlMode method is async because in other circumstances, I do the following:
//await Task.Delay(5000); // sleep 5 seconds
}
// wait while all test running threads complete
try
{
Task.WaitAll(runningTasks.ToArray());
}
catch (Exception)
{
// not really a catch all handler...
}
}
catch (Exception)
{
// not really a catch all handler...
}
return result;
}
private static void Begin(string site)
{
//runningTasks.Add(Task.Factory.StartNew(() => HandlePlayback(runArgs)));
runningTasks.Add(Task.Factory.StartNew((args) => HandlePlayback((string)((object[])args)[0]), new object[] { site }));
}
private static async Task<int> HandlePlayback(string site)
{
int result = 1;
try
{
PlaybackEngine engine = new PlaybackEngine(site);
bool runResult = await engine.RunCommandLine(site);
if (!runResult)
{
result = 0;
}
}
catch (Exception)
{
result = 0;
}
return result;
}
}
public class PlaybackEngine
{
private static HttpClientHandler ClientHandler = new HttpClientHandler()
{
AllowAutoRedirect = false,
AutomaticDecompression = System.Net.DecompressionMethods.GZip | DecompressionMethods.Deflate
};
private static HttpClient Client = new HttpClient(ClientHandler);
public string Target { get; set; }
public PlaybackEngine(string target)
{
Target = target;
}
public async Task<bool> RunCommandLine(string site)
{
bool success = true;
string response = await this.SendRequest();
return success;
}
private async Task<string> SendRequest()
{
string result = string.Empty;
string requestTarget = Target;
HttpMethod method = HttpMethod.Post;
var message = new HttpRequestMessage(method, requestTarget);
StringContent requestContent = null;
requestContent = new StringContent("dummycontent", Encoding.UTF8, "application/x-www-form-urlencoded");
message.Content = requestContent;
try
{
using (HttpResponseMessage response = await Client.SendAsync(message))
{
using (HttpContent responseContent = response.Content)
{
result = await responseContent.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(result);
}
}
}
catch (Exception ex)
{
}
return result;
}
}
}
UPDATE2:
I put similar code online at http://rextester.com/CJS33330
It's a straight console app, and I've added .ConfigureAwait(false) to all awaits (with no effect). In separate testing, I tried 4 or 5 other ways to call the first async function from Main - which all worked but had the same behavior.
The problem with this code is that I am not waiting on the Tasks that I thought I was. The runningTasks collection accepts any kind of Task. I didn't realize that Task.Factory.StartNew returned different type than the Task I was trying to start. My function returns
Task<int>
but StartNew returns
Task<Task<int>>
Those tasks completed immediately, and so the main thread did not stay alive long enough for the actual routines to run. You have to wait on the inner task instead:
Task<Task<int>> wrappedTask = Task.Factory.StartNew(...);
Task<int> t = await wrappedTask;
runningTasks.Add(t);
...
Task allTasks = Task.WhenAll(runningTasks.ToArray());
await allTasks;
For some reason, I was not able to use the built in ".Unwrap" function that should be equivalent, but the above code does the job.
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