I am writing below code in WPF. However, it does not run asynchronously. I have commented the line where the code gets blocked. Please let me know where am I doing the mistake.
private async void txtSampleRequest_Click(object sender, RoutedEventArgs e)
{
await DownloadData();
}
async Task<string> DownloadData()
{
string authorization = txtAuthentication.Text;
string cookie = txtCookie.Text;
try
{
var vvv = Enumerable.Range(0, 50);
List<Task> TaskList = new List<Task>();
foreach (int s in vvv)
{
Task LastTask = ZerodhaOperations.MyHttpRequest("", authorization, cookie, "5minute");//Even this method is async method, and I have added dummy Task.Delay(1) to run method asynchronously. Below is the full definition of this class.
TaskList.Add(LastTask);
}
await Task.WhenAll(TaskList); //<------------ Here it stops working asynchronously and blocks UI of application.
MessageBox.Show("DONE");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return "";
}
class ZerodhaOperations
{
public static async Task<string> MyHttpRequest(string WEBSERVICE_URL,string authorization, string cookie,string timeFrame)
{
await Task.Delay(1);
if (authorization == "" || cookie == "" || authorization == null || cookie == null)
{
throw new ArgumentException("Aditya Says, No Authorization and cookie has been set");
}
string jsonResponse = "";
WEBSERVICE_URL = "https://kite.zerodha.com/oms/instruments/historical/519937/timeFrame?user_id=YD0744&oi=1&from=2020-04-24&to=2020-04-24";
WEBSERVICE_URL = $"https://kite.zerodha.com/oms/instruments/historical/806401/{timeFrame}?user_id=YD0744&oi=1&from=2020-12-03&to=2020-12-03";
try
{
var webRequest = System.Net.WebRequest.Create(WEBSERVICE_URL);
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
if (webRequest != null)
{
webRequest.Method = "GET";
webRequest.Timeout = 5000;
if (authorization != "")
{
webRequest.Headers.Add("authority", "kite.zerodha.com");
webRequest.Headers.Add("authorization", authorization);
}
webRequest.Headers.Add("cookie", cookie);
webRequest.Headers.Add("method", "GET");
using (System.IO.Stream s = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
{
jsonResponse = sr.ReadToEnd();
if (jsonResponse == "")
throw new Exception("Empty Response");
//MessageBox.Show(jsonResponse);
}
}
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Empty Response"))
throw ex;
else
MessageBox.Show(ex.ToString());
}
return jsonResponse;
}
}
The way await works is that it captures a "context" when it asynchronously yields. The details are in the link, but in this case the asynchronous method is running on the WPF UI thread, so it captures a context that will resume running on that UI thread.
So the await Task.Delay(1) is in fact forcing MyHttpRequest to act asynchronously, but it's what happens next that is tripping you up. MyHttpRequest does the await, which captures the UI context and returns an incomplete task. This happens multiple times in DownloadData, which collects the incomplete tasks and then awaits the Task.WhenAll of them. So then DownloadData returns an incomplete task which is awaited by the event handler, which returns control back to the WPF message loop.
Now, what happens next is that those Task.Delay timers fire off almost immediately. So, MyHttpRequest resumes its async method, and since its await captured that UI context, it resumes running on the UI thread. So the await Task.Delay(1) did cause a yield to the WPF message loop, but then practically the very next thing the UI thread has to do is resume those async methods.
And the remainder of those async methods are synchronous, blocking the UI thread.
So, to solve this, you can make the methods truly asynchronous. Delete the await Task.Delay(1) completely and instead replace the synchronous APIs with asynchronous ones (GetResponseAsync, ReadToEndAsync). Or if you want to modernize the code further, replace WebRequest with HttpClient.
Your other option is to keep the code synchronous and just use Task.Run to run the synchronous code on background threads. Again, you would delete the await Task.Delay(1), and this time you would change the method signature to be synchronous. Then you can wrap the call to the (synchronous) MyHttpRequest in a Task.Run, e.g., Task LastTask = Task.Run(() => ZerodhaOperations.MyHttpRequest("", authorization, cookie, "5minute")); The Task.Run solution is less ideal than the asynchronous solution, but it is acceptable if you have a lot of other code that still needs MyHttpRequest to be synchronous for now.
Note that either way, the usage of Task.Delay(1) is wrong. It does not "run [the] method asynchronously". The proper way to run a method asynchronously is to have it call asynchronous APIs and not call blocking APIs.
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 need to do a RestRequest and get some JSON, I am not sure if my method is really async since there is still a little freeze in my UI when I use this method.
public async Task<List<MyObject>> Load()
{
var tcs = new TaskCompletionSource<List<Myobject>>();
var client = new RestSharp.RestClient("https://exampleapi.com");
client.Authenticator = OAuth1Authenticator.ForProtectedResource(
[...]);
var request = new RestSharp.RestRequest("examp.json", Method.GET);
client.ExecuteAsync(request, response =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
List_ = new List<MyObject>();
List_ = JsonConvert.DeserializeObject<List<MyObject>>(response.Content);
tcs.SetResult(List_);
}
else
{
MessageBox.Show("Error");
}
});
return await tcs.Task;
}
Specially for this line of code :
List_ = JsonConvert.DeserializeObject<List<MyObject>>(response.Content);
is it really async ? because it seems to block the the UI . Can you tell me how can I make this function properly async ?
It seems the delegate passed as an argument to ExecuteAsync is being executed on the UI thread. If that is the case, simply use Task.Run to run the delegate on the threadpool instead.
client.ExecuteAsync(request, async (response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
var list = await Task.Run( () => JsonConvert.DeserializeObject<List<MyObject>>(response.Content));
tcs.SetResult(list);
}
else
{
MessageBox.Show("Error");
}
});
Is List_ a field? It seems to me like it should be a local variable. Also, there's no need to initialize it with an empty list before deserializing the json.
JsonConvert.DeserializeObject is synchronous. You can tell by the fact that it returns you the result of its computation immediately. There is no way it could do something "in the background" and only later hand you the result.
Move CPU bound work to a thread-pool thread using Task.Run. You can move the whole REST request there if that is more convenient to you.
Note, that your message box call should run on the UI thread. Better not create a message box on a thread-pool thread like you are doing at the moment. That will result in two UI threads. The message box will not be modal.
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.
I've an extension method for WebClient (WP8)
public static Task<string> DownloadStringTask(this WebClient webClient, Uri uri)
{
var tcs = new TaskCompletionSource<string>();
webClient.DownloadStringCompleted += (s, e) =>
{
if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(e.Result);
}
};
webClient.DownloadStringAsync(uri);
return tcs.Task;
}
and the call to this method
public string GetResult()
{
var task = new WebClient().DownloadStringTask(new Uri("http:\\www.foo.com"));
return task.Result;
}
The DownloadStringCompleted is never executed and obviously there is no result, if I press the pause button on VS always is waiting in task.Result.
Any idea?
Thanks in advance.
Is GetResult executed from the main thread? In that case, it may be a deadlock. If I remember correctly, the callback of the WebClient is executed on the main thread, which cannot happen since you're blocking it by calling task.Result.
You have multiple ways to prevent this issue:
Use a HttpWebRequest instead of a WebClient
Call GetResult from another thread
Execute the task asynchronously by using task.ContinueWith instead of directly task.Result
Rewrite your method using async/await keywords
task.Result blocks the executing thread until the result is available. For me it seems that your code just defeats the purpose of making the request asynchronous. As KooKiz mentioned, use some truly asynchronous API to get the result, such as task.ContinueWith or await task.