Deadlock at consuming rest API [duplicate] - c#

This question already has answers here:
await vs Task.Wait - Deadlock?
(3 answers)
Closed 5 years ago.
So I built a small program to consume a REST api, but it never finishes beacuse no data is received. I'm new to using Async and wait commands, so I have probably gotten it wrong somehow. The threads responsible for retrieving the data just times out after a couple of seconds. The url in itself is valid though and no exception seems to be thrown.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Bot_Application1
{
[Serializable]
public class ConsumeFOAAS
{
private static HttpClient client = new HttpClient();
private static String message = "empty";
public String GetMessage()
{
RunAsync().Wait();
return message;
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://foaas.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
try
{
message = await GetProductAsync("/because/:Insultinator");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static async Task<String> GetProductAsync(string path)
{
Debug.WriteLine("Inside GetProductAsync");
String product = "empty";
HttpResponseMessage response = await client.GetAsync(path); //<--This never finishes
Debug.WriteLine("Response: " + response);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsStringAsync();
}
return product;
}
}
}

You shouldn't call .Wait(). The best wait to achieve what you want is :
public async Task<String> GetMessage()
{
return await RunAsync();
}
And change the RunAsync function :
static async Task<string> RunAsync() {
client.BaseAddress = new Uri("http://foaas.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
try {
return await GetProductAsync("/because/:Insultinator");
} catch (Exception e) {
Console.WriteLine(e.Message);
}
}

Deadlock can be caused by task manager. Try using:
RunAsync().ConfigureAwait(false).Wait();
Explanation: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

Related

Is it better to use HttpClient or WebRequest in ASP.NET Core to read the content of a file line by line asynchronously remotely?

I plan to read a remote file line by line asynchronously using https://github.com/Dasync/AsyncEnumerable (since there is not yet Async Streams [C# 8 maybe]: https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md):
public static class StringExtensions
{
public static AsyncEnumerable<string> ReadLinesAsyncViaHttpClient(this string uri)
{
return new AsyncEnumerable<string>(async yield =>
{
using (var httpClient = new HttpClient())
{
using (var responseStream = await httpClient.GetStreamAsync(uri))
{
using (var streamReader = new StreamReader(responseStream))
{
while(true)
{
var line = await streamReader.ReadLineAsync();
if (line != null)
{
await yield.ReturnAsync(line);
}
else
{
return;
}
}
}
}
}
});
}
public static AsyncEnumerable<string> ReadLinesAsyncViaWebRequest(this string uri)
{
return new AsyncEnumerable<string>(async yield =>
{
var request = WebRequest.Create(uri);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
while(true)
{
var line = await streamReader.ReadLineAsync();
if (line != null)
{
await yield.ReturnAsync(line);
}
else
{
return;
}
}
}
}
}
});
}
}
It seems that they both run just fine in a simple Console application like below:
public class Program
{
public static async Task Main(string[] args)
{
// Or any other remote file
const string url = #"https://gist.githubusercontent.com/dgrtwo/a30d99baa9b7bfc9f2440b355ddd1f75/raw/700ab5bb0b5f8f5a14377f5103dbe921d4238216/by_tag_year.csv";
await url.ReadLinesAsyncViaWebRequest().ForEachAsync(line =>
{
Console.WriteLine(line, Color.GreenYellow);
});
await url.ReadLinesAsyncViaHttpClient().ForEachAsync(line =>
{
Console.WriteLine(line, Color.Purple);
});
}
}
... but I have some concerns if it is used as part of an ASP.NET Core WebAPI to process the lines and then push them using PushStreamContent:
https://learn.microsoft.com/en-us/previous-versions/aspnet/hh995285(v=vs.108)
https://blog.stephencleary.com/2016/10/async-pushstreamcontent.html
The idea would be to have a pipeline of data which leverages async / await so that the number of threads in use is as low as possible and also to avoid an increase in memory (which leverage the enumerable-like feature of AsyncEnumerable).
I read several articles but it seems it's all non .NET Core versions and I don't really know if there would be some potential performance issues / caveats in regard to what I would like to achieve?
Difference between HttpRequest, HttpWebRequest and WebRequest
http://www.diogonunes.com/blog/webclient-vs-httpclient-vs-httpwebrequest/
An example of "business" case would be:
using System;
using System.Collections.Async;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace WebApplicationTest.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DumbValuesController : ControllerBase
{
private static readonly Random Random = new Random();
// GET api/values
[HttpGet]
public async Task<IActionResult> DumbGetAsync([FromQuery] string fileUri)
{
using (var streamWriter = new StreamWriter(HttpContext.Response.Body))
{
await fileUri.ReadLinesAsyncViaHttpClient().ForEachAsync(async line =>
{
// Some dumb process on each (maybe big line)
line += Random.Next(0, 100 + 1);
await streamWriter.WriteLineAsync(line);
});
}
return Ok();
}
}
}
We have access to the source code for .NET Core. So you can look.
The underlying implementation of both end up using HttpClientHandler (the implementation of that class is split up into 4 files).
You can see this from the source code of both HttpClient and HttpWebRequest (which WebRequest uses).
So I suspect you won't notice any difference in the performance of either.
HttpClient is the latest one to be written, so that's why its use is encouraged. And for the reasons mentioned in the article you linked to: http://www.diogonunes.com/blog/webclient-vs-httpclient-vs-httpwebrequest/
With the latest release of .Net Core 6.0, WebRequest will be declared as deprecated. Microsoft recommended to use HttpClient instead
https://learn.microsoft.com/en-us/dotnet/core/compatibility/networking/6.0/webrequest-deprecated

Get JSON from url, save to file, and finally CreateActivationService

Using the Template Studio extension for Visual Studio, I generated a project solution base and am now attempting to interject the app load process with a HTTP request before proceeding the render the page view.
App.xaml.cs
using System;
using Braytech_3.Services;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
namespace Braytech_3
{
public sealed partial class App : Application
{
private Lazy<ActivationService> _activationService;
private ActivationService ActivationService
{
get { return _activationService.Value; }
}
public App()
{
InitializeComponent();
APIRequest();
// Deferred execution until used. Check https://msdn.microsoft.com/library/dd642331(v=vs.110).aspx for further info on Lazy<T> class.
_activationService = new Lazy<ActivationService>(CreateActivationService);
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
if (!args.PrelaunchActivated)
{
await ActivationService.ActivateAsync(args);
}
}
protected override async void OnActivated(IActivatedEventArgs args)
{
await ActivationService.ActivateAsync(args);
}
private async void APIRequest()
{
//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;
Uri requestUri = new Uri("https://json_url");
//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";
try
{
//Send the GET request
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
APITempSave(httpResponseBody);
}
catch (Exception ex)
{
}
}
private async void APITempSave(string json)
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
if (await tempFolder.TryGetItemAsync("APIData.json") != null)
{
StorageFile APIData = await tempFolder.GetFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
else
{
StorageFile APIData = await tempFolder.CreateFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
}
private ActivationService CreateActivationService()
{
return new ActivationService(this, typeof(Views.VendorsPage), new Lazy<UIElement>(CreateShell));
}
private UIElement CreateShell()
{
return new Views.ShellPage();
}
}
}
I think what I need to do is call _activationService = new Lazy<ActivationService>(CreateActivationService); once APITempSave() has been called but I am unsure of how to do so and what best practices are.
Any guidance would be greatly appreciated!
After further investigation and familiarisation with the generated solution, as well as additional Googling of await, async, and Tasks<>, I was able to implement the request as a service alongside items such as ThemeSelector, and ToastNotifications.
The ThemeSelector is one of the first things to be called in order to determine light and dark theme mode for the current user, so I was able to model my service around it and call it at the same time.
This is obviously very specific to the code that template studio generates, but some concepts are shared and should anyone else look for similar answers in the future maybe they'll find this.
APIRequest.cs (Service)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
namespace Braytech_3.Services
{
public static class APIRequest
{
internal static async Task Request()
{
//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;
Uri requestUri = new Uri("https://json_url");
//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";
try
{
//Send the GET request
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
await APITempSave(httpResponseBody);
}
catch (Exception ex)
{
}
}
internal static async Task APITempSave(string json)
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
if (await tempFolder.TryGetItemAsync("APIData.json") != null)
{
StorageFile APIData = await tempFolder.GetFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
else
{
StorageFile APIData = await tempFolder.CreateFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
}
}
}
ActiviationService.cs (originally called by App.xaml.cs)
private async Task InitializeAsync()
{
await ThemeSelectorService.InitializeAsync();
await APIRequest.Request();
}

Using await on HttpClient in wpf project

I just following a sample in calling HttpClient in sync mode, it works fine in a Console application.
However, when I move it to a wpf application, the program hanged without any return.
I try to isolate the problem by building a separate class to handle a dummy request to visit www.google.com.
It seems that the application hang in calling client.GetAsync, may I know if there has any thing need to be changed from console application to wpf in this case?
Please find the source of both the console application and wpf as below,
Console application - works fine:
using System;
using System.Threading.Tasks;
using System.Net.Http;
namespace ca03
{
static class action
{
static async Task<string> DownloadPageAsync()
{
// ... Target page.
string page = "http://www.google.com/";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null &&
result.Length >= 50)
{
Console.WriteLine(result.Substring(0, 50) + "...");
}
return result;
}
}
public static string goDownload()
{
Task<string> x = DownloadPageAsync();
string result = x.Result;
return result;
}
}
class Program
{
static void Main(string[] args)
{
string data = action.goDownload();
Console.WriteLine(data);
Console.ReadLine();
}
}
}
WPF application: (just a plain project with a button added) - hang in GetAsync
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Net.Http;
namespace wpf02
{
static class action
{
static async Task<string> DownloadPageAsync()
{
// ... Target page.
string page = "http://www.google.com/";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
return result;
}
}
public static string goDownload()
{
Task<string> x = DownloadPageAsync();
string result = x.Result;
return result;
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
string data = action.goDownload();
Console.WriteLine(data);
}
}
}
This line is the problem:
string result = x.Result;
When you use the Result property, that blocks the current thread until the task has completed. That's a problem when your continuations are meant to run on the same thread...
Basically, there's no point in your goDownload method - just change your DownloadPageAsync method to be public, then change your button_Click method to also be async so you can await the result:
private async void button_Click(object sender, RoutedEventArgs e)
{
string data = await action.DownloadPageAsync();
Console.WriteLine(data);
}
Top-level reason of this - WPF ( as any UI app) have a UI-thread, that work with winwdows loop queue. Jon Skeet give you a simplest desigion, but you can use thread from CLR ThreadPool:
Task<string> t = Task.Run(() => DownloadPageAsync());
string result = = await t;

Difference between HttpClient.GetStringAsync and WebClient.DownloadStringAsync

I have the following code
static void Main(string[] args)
{
string url = "http://www.google.com";
Console.WriteLine(GetUrl(url).Result); // throws TaskCanceledException
Console.WriteLine(GetUrl2(url).Result);
}
public static Task<string> GetUrl(string url)
{
using (var client = new HttpClient())
{
return client.GetStringAsync(url);
}
}
public static Task<string> GetUrl2(string url)
{
using (var client = new WebClient())
{
return client.DownloadStringTaskAsync(url);
}
}
I'm trying to get the string of an url, the problem is GetUrl method (uses HttpClient's GetStringAsync) throws an TaskCacelledException, but GetUrl2 method (uses WebClient's DownloadStringTaskAsync) runs correctly. Is this caused due to using statement? What am I missing?
Edit. In this example I'm calling Result on the task because this is a console application, I know that it is best to await the result in a event handler for example.
Is this caused due to using statement?
Yes. In both code examples, you're disposing the underlying client before the operation completes. Both code examples should be changed as such:
public static async Task<string> GetUrlAsync(string url)
{
using (var client = new HttpClient())
{
return await client.GetStringAsync(url);
}
}
public static async Task<string> GetUrl2Async(string url)
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(url);
}
}
The behavior of asynchronous downloads when their underlying clients are disposed is undocumented. It's best not to dispose the clients until your code is done using them.

Async task does not end

I'm trying to start async task (on .NET 4.5) which downloads content of web page, but somehow this task never finishes.
My PageDownloader class:
using System.Net;
using System.Text;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System;
namespace ParserConsole.WebClient
{
public class PageDownloader
{
private System.Net.Http.HttpClient _client;
public PageDownloader()
: this(Encoding.UTF8) { }
private Encoding _encoding;
public PageDownloader(Encoding encoding)
{
_encoding = encoding;
_client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10)};
}
private HttpRequestMessage _request;
private HttpResponseMessage _response;
private string _responseString;
public string GetPageData(string link)
{
_request = new HttpRequestMessage(HttpMethod.Get, link);
_request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
_request.Headers.Add("Accept", "text/html");
GetResponse().Wait();
GetStringFromResponse().Wait();
return _responseString;
}
private async Task<HttpResponseMessage> GetResponse() {
return _response = await _client.GetAsync(_request.RequestUri);
}
private async Task<string> GetStringFromResponse() {
return _responseString = await _response.Content.ReadAsStringAsync();
}
}
}
I start downloading page by calling
new PageDownloader().GetPageData(url);
When I'm trying to debug the code, everything is fine till GetResponse().Wait(). But somehow GetResponse() task never finishes - breakpoint on the next line is never reached. I get no exceptions, application continues running. Any suggestions?
This is a standard deadlock condition you get when you start an async operation and then block on the returned task.
Here is a blog post discussion the topic.
Basically, the await call ensures that the continuation it wires up of the task will run in the context you were originally in (which is very helpful) but because you are calling Wait in that same context it's blocking, so the continuation never runs, and that continuation needs to run for the wait to end. Classic deadlock.
As for the fix; usually it means you just shouldn't be doing a blocking wait on the async operation; it's contrary to the design of the whole system. You should, "async all the way up". In this case it would mean that GetPageData should return a Task<string> rather than a string, and rather than waiting on the other operations that return a task you should await on them.
Now, having said that, there are ways of doing a blocking wait on the async operations without deadlocking. While it can be done, it honestly defeats the purpose of using async/await in the first place. The primary advantage of using that system is that the main context isn't blocked; when you block on it that entire advantage goes away, and you might as well just use blocking code all the way through. async/await is really more of an all-or-nothing paradigm.
Here is how I would structure that class:
public class PageDownloader
{
private System.Net.Http.HttpClient _client;
private Encoding _encoding;
public PageDownloader()
: this(Encoding.UTF8) { }
public PageDownloader(Encoding encoding)
{
_encoding = encoding;
_client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) };
}
public async Task<string> GetPageData(string link)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, link);
request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
request.Headers.Add("Accept", "text/html");
HttpResponseMessage response = await _client.GetAsync(request.RequestUri);
return await response.Content.ReadAsStringAsync(); ;
}
}
Why not just do this if you want to have a function like that.
public string GetPageData(string link)
{
_request = new HttpRequestMessage(HttpMethod.Get, link);
_request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
_request.Headers.Add("Accept", "text/html");
var readTask = _client.GetStringAsync(link);
readTask.Wait();
return readTask.Result;
}
It would be better to return the Task all the way back and handle it with async/await in the calling code.
public Task<string> GetPageData(string link)
{
_request = new HttpRequestMessage(HttpMethod.Get, link);
_request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
_request.Headers.Add("Accept", "text/html");
return _client.GetStringAsync(link);
}

Categories

Resources