Is it possible that async code takes more time than sync? - c#

I have a piece of code that execute HTTP requests (inside a loop) from some currency API and do some work on the data which received.
Because it was slow, I changed it to asynchronously function using: Task.Run()
However, when I measure calculation time (using Stopwatch..) of the function
with the async method, it takes more time (or even) than the sync one.
How can it possible? Am I do it wrong?
[HttpPost,Route("GetCurrenciesData")]
public IHttpActionResult GetCurrenciesData(InputCurrenciesData cls)
{
try
{
var watch = Stopwatch.StartNew();
var data2 = GetBL.mySyncMethod(cls.currenciesList);
watch.Stop();
string first = watch.Elapsed.ToString(); // --> this faster
watch = Stopwatch.StartNew();
var data = GetBL.myAsyncMethod(cls.currenciesList);
string second = watch.Elapsed.ToString(); // --> this slower
return Ok(data);
}
catch (Exception ex)
{
getGeneric.WriteError(ex.Message, ex.StackTrace);
return BadRequest();
}
}
Example code of the sync function:
public GenericClass mySyncMethod(string[] currencies)
{
try
{
foreach (string currency in currencies)
{
getDataFromApi(currency);
}
return getDataChart;
}
catch (Exception ex)
{
WriteError(ex.Message, ex.StackTrace);
return null;
}
}
Example of the async :
public GenericClass myAsyncMethod(string[] currencies)
{
try
{
List<Task> TaskList = new List<Task>();
foreach (string currency in currenies)
{
var myTask = new Task(() =>
{
getDataFromApi(currency);
});
myTask.Start();
TaskList.Add(myTask);
}
Task.WaitAll(TaskList.ToArray());
return getDataChart;
}
catch (Exception ex)
{
WriteError(ex.Message, ex.StackTrace);
return null;
}
}
The code of the function getDataFromApi:
private void getDataFromApi(string currency)
{
string currencyCode = getCurrenciesDictionary[currency];
//Get api's URL from Web.config and concatenation the wanted currency code
string URL = string.Format(GetConfig("Api"), currencyCode);
/*HTTP request to retrieve data from Api*/
using (HttpClientHandler handler = new HttpClientHandler())
{
handler.UseDefaultCredentials = true;
using (HttpClient httpClient = new HttpClient(handler))
{
var ResultAsync = httpClient.GetAsync(URL).Result;
if (ResultAsync.IsSuccessStatusCode)
{
var content = ResultAsync.Content;
string Data = content.ReadAsStringAsync().Result;
try
{
var ret = JsonConvert.DeserializeObject<RootObject>(Data);
/*add new class to currencyRate list - class which represent the currency and its rates*/
getDataChart.CurrencyRate.Add(new CurrencyRate
{
Currency = currency,
Rates = ret.dataset.data.AsEnumerable().
Select(date => Double.Parse(date[1].ToString())).ToList()
});
}
catch (Exception ex)
{
WriteError(ex.Message + " currency: " + currency + "/n/ URL: " + URL, ex.StackTrace);
}
}
}
}

try and read some of this guy:
http://blog.stephencleary.com/2012/02/async-and-await.html
http://blog.stephencleary.com/2016/12/eliding-async-await.html
he's telling in a few words how it works. The main point is that you get some overhead using async/await, however, you're freeing resources. So any task that will run, will run on the synchronized context. but you can handle much more request.
Besides the fact that you are not really using async functionality with the .result kills actually the async part.
In a short line, doing it async doesn't necessarily speed it up. If you have more CPU-bound tasks, that can run simultaneously you will major performance, however, in your case, you will not performance, but resources like Kevin tells in his comments.
hope it helps!

Related

Performance hit when converting IEnumerable to List of Tasks

public async Task<List<usp_TeamMeetingGetAll_Result>> TeamsMeetingReportGetAll(bool activeOnly)
{
List<usp_TeamMeetingGetAll_Result> teamMeetingGetAllResults = _db
.usp_TeamMeetingGetAll(activeOnly)
.ToList();
IEnumerable<Task<usp_TeamMeetingGetAll_Result>> taskListQuery =
from teamMeetingGetAllResult in teamMeetingGetAllResults
select TeamsMeetingGet(teamMeetingGetAllResult);
List<Task<usp_TeamMeetingGetAll_Result>> taskList = taskListQuery.ToList();
usp_TeamMeetingGetAll_Result[] results = await Task.WhenAll(taskList);
return results.OrderBy(r => r.DisplayStartDate).ToList();
}
I'm trying to figure out why line 3 (the taskList assignment) takes 7-8 seconds to execute. Line 1 calls a stored proc and puts the results (26 rows, about 15 columns) in a list and takes about 1.5 seconds. Line 2 takes less than a millisecond. Line 4 takes about 4 seconds for all async tasks to complete.
Edit: Okay, thanks to the answers below I understand that:
List<Task<usp_TeamMeetingGetAll_Result>> taskList = taskListQuery.ToList();
is causing calls to TeamsMeetingGet. I should have checked that before.
TeamsMeetingGet is making a aync call to a web API, waiting for the results, populating some properties of the usp_TeamMeetingGetAll_Result object with the results, and passing it back.
public async Task<usp_TeamMeetingGetAll_Result> TeamsMeetingGet(usp_TeamMeetingGetAll_Result teamMeetingGetAllResult)
{
//OnlineMeeting onlineMeeting = await TeamsMeetingGet(teamMeetingGetAllResult.JoinWebUrl);
HttpClient client = GraphHelper.GetAuthenticatedHttpClient();
string request =
"users/xxxxxxxxxx/onlineMeetings?$filter=JoinWebUrl%20eq%20'" + HttpUtility.UrlEncode(teamMeetingGetAllResult.JoinWebUrl) + "'";
var response = await client.GetAsync(request);
var result = await response.Content.ReadAsStringAsync();
var jresult = JObject.Parse(result);
var onlineMeetings = jresult["value"].ToObject<OnlineMeeting[]>();
if (onlineMeetings[0].Id != null)
{
teamMeetingGetAllResult.NumAttendees = onlineMeetings[0].Participants.Attendees.Count();
teamMeetingGetAllResult.Subject = onlineMeetings[0].Subject;
//More assignments
}
else
{
teamMeetingGetAllResult.NumAttendees = 0;
teamMeetingGetAllResult.Subject = "";
}
return teamMeetingGetAllResult;
}
If I have 100 rows, I'd like to call TeamsMeetingGet 100 times simultaneously, instead of synchronously. Is this how I do it?
I asked for the GetAuthenticatedHttpClient code and got back:
public class GraphHelper
{
private static string _graphClientSecret = ConfigurationManager.AppSettings["graphClientSecret"];
private static string _graphClientId = ConfigurationManager.AppSettings["graphClientId"];
private static string _graphTenantId = ConfigurationManager.AppSettings["graphTenantId"];
public static HttpClient GetAuthenticatedHttpClient()
{
IConfidentialClientApplication app;
var clientId = _graphClientId;
var tenantID = _graphTenantId;
app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(_graphClientSecret)
.WithAuthority(new Uri("https://login.microsoftonline.com/" + tenantID))
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = app.AcquireTokenForClient(scopes)
.ExecuteAsync().Result;
}
catch (MsalUiRequiredException ex)
{
// The application doesn't have sufficient permissions.
// - Did you declare enough app permissions during app creation?
// - Did the tenant admin grant permissions to the application?
throw ex;
}
catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
{
// Invalid scope. The scope has to be in the form "https://resourceurl/.default"
// Mitigation: Change the scope to be as expected.
throw ex;
}
catch (Exception ex)
{
throw ex;
}
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", result.AccessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com/v1.0/");
return client;
}
}

Recursive method is returning first iteration object C#

static void Main(string[] args)
{
token objtoken = new token();
var location = AddLocations();
OutPutResults outPutResultsApi = new OutPutResults();
GCPcall gCPcall = new GCPcall();
OutPutResults finaloutPutResultsApi = new OutPutResults();
var addressdt = new AddressDataDetails();
finaloutPutResultsApi.addressDatas = new List<AddressDataDetails>();
Console.WriteLine("Hello World!");
List<string> placeId = new List<string>();
var baseUrl = "https://maps.googleapis.com/maps/api/place/textsearch/json?";
var apiKey = "&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".Trim();
foreach (var itemlocations in location)
{
var searchtext = "query=" + itemlocations.Trim();
var finalUrl = baseUrl + searchtext + apiKey;
gCPcall.RecursiveApiCall(finalUrl, ref placeId, objtoken.NextToken);
}
var ids = gCPcall.myPalceid;
}
public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
{
try
{
var token = "&pagetoken=" + nextToken;
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(finalUrl + token);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsStringAsync();
readTask.Wait();
var students = readTask.Result;
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
placeId.Add(item.place_id);
}
}
}
if (nextToken != null)
{
RecursiveApiCall(finalUrl, ref placeId, nextToken);
}
return placeId;
}
catch (Exception ex)
{
throw;
}
}
My recursive method has some issue. Here whenever I am debugging this code it work fine. It goes in recursive call twice.
As debugging result I am getting list place_id with 20 items in first call and next call 9 items total 29 items in place_id object which is correct in static main method.
But if I run without debugging mode I am getting only 20 place_id. next recursive iteration data is missing even if it has next valid token.
I don't have any clue why this is happening. Can someone tell me what is the issue with my code?
Here are my suggestions, which may or may not solve the problem:
// First of all, let's fix the signature : Go async _all the way_
//public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
// also reuse the HttpClient!
public async Task ApiCallAsync(HttpClient client, string finalUrl, List<string> placeId, string nextToken = null)
{
// Loop, don't recurse
while(!(nextToken is null)) // C# 9: while(nextToken is not null)
{
try
{
var token = "&pagetoken=" + nextToken;
// again async all the way
var result = await client.GetAsync(finalUrl+token);
if (result.IsSuccessStatusCode)
{
// async all the way!
var students = await result.Content.ReadAsStringAsync();
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
// Will be reflected in main, so no need to return or `ref` keyword
placeId.Add(item.place_id);
}
}
// NO recursion needed!
// if (nextToken != null)
// {
// RecursiveApiCall(finalUrl, ref placeId, nextToken);
// }
}
catch (Exception ex)
{
// rethrow, only is somewhat useless
// I'd suggest using a logging framework and
// log.Error(ex, "Some useful message");
throw;
// OR remove try/catch here all together and wrap the call to this method
// in try / catch with logging.
}
}
Mind that you'll need to make your main :
async Task Main(string[] args)
and call this as
await ApiCallAsync(client, finalUrl, placeId, nextToken);
Also create an HttpClient in main and reuse that:
"HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors." - Remarks
... which shouldn't do much harm here, but it's a "best practice" anyhow.
Now, as to why you get only 20 instead of expected 29 items, I cannot say if this resolves that issue. I'd highly recommend to introduce a Logging Framework and make log entries accordingly, so you may find the culprid.

HttpClient in while loop only executed once

I am trying to call HttpClient request inside for loop as follows. It needs to do multiple consecutive calls to third party rest api.
But it only gives me fist service call result while loop exit before getting result from rest of the service call.
private void Search()
{
try
{
var i = 1;
using (var httpClient = new HttpClient())
{
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
var response = httpClient.GetAsync(url).Result;
string jsonResult = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(jsonResult.ToString());
i++;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
When I run with debug points the program gives me all the result. But when I run it without debug points it gives me only the first result.
I tried this with using async, await methods too. It also gives me same result.
As I feel Program needs to wait until the async call returns data.
Please help me to solve this.
EDIT - async way
private async Task<string> SearchNew()
{
try
{
var i = 1;
var res = string.Empty;
using (var httpClient = new HttpClient())
{
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
var response = httpClient.GetAsync(url).Result;
string jsonResult = await response.Content.ReadAsStringAsync();
res = res + jsonResult + " --- ";
i++;
}
}
return res;
}
catch (Exception ex)
{
return ex.Message;
}
}
Both are giving same result.
There's a few things here that you should be doing. First, move the HttpClient creation outside of your method and make it static. You only need one of them and having multiple can be really bad for stability (see here):
private static HttpClient _client = new HttpClient();
Next, extract the calls to the HttpClient into a single method, something simple like this:
//Please choose a better name than this
private async Task<string> GetData(string url)
{
var response = await _client.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
And finally, you create a list of tasks and wait for them all to complete asynchronously using Task.WhenAll:
private async Task<string[]> SearchAsync()
{
var i = 1;
var tasks = new List<Task<string>>();
//Create the tasks
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
tasks.Add(GetData(url));
i++;
}
//Wait for the tasks to complete and return
return await Task.WhenAll(tasks);
}
And to call this method:
var results = await SearchAsync();
foreach (var result in results)
{
Console.WriteLine(result);
}

Data structure to use when gathering response from async calls

I am running this piece of code in my application.
public Task<BulkResponse<JObject>> GetRelatedObjectsAsync(IEnumerable<PrimaryObjectInfo> primaryObjectInfos)
{
var allSecondaries = new List<Tuple<int, List<JObject>>>();
var exceptionsDict = new ConcurrentDictionary<int, Exception>();
var relatedObjectsTasks = primaryObjectInfos.Select(async primaryObjectInfo =>
{
try
{
var secondaryObject = await objectManager.GetRelatedObjectsAsync(primaryObjectInfo);
allSecondaries.Add(Tuple.Create(primaryObjectInfo.Index, secondaryObject.ToList()));
}
catch (Exception ex)
{
exceptionsDict.TryAdd(primaryObjectInfo.Index, ex);
}
});
await Task.WhenAll(relatedObjectsTasks);
return ConvertToBulkResponse(allSecondaries, exceptionsDict);
}
When I run this code allSecondaries object sometimes returns valid list of results and sometimes the code ends up catching exceptions for the parallel threads I have for each primaryObjectInfo.
Async method objectManager.GetRelatedObjectsAsync() internally call 4-5 async functions and there are functions where parameters are passed by reference. (ref keyword)
Question:
Am I using the right data structure to consolidate the result from all parallel threads ?
If yes, what could be the reason I am getting different result every time?
You'd better split the execution from the results gathering:
public Task<BulkResponse<JObject>> GetRelatedObjectsAsync(
IEnumerable<PrimaryObjectInfo> primaryObjectInfos)
{
var relatedObjectsTasks = primaryObjectInfos
.Select(
async primaryObjectInfo =>
(primaryObjectInfo.Index,
await objectManager.GetRelatedObjectsAsync(primaryObjectInfo)))
.ToList();
try
{
await Task.WhenAll(relatedObjectsTasks);
}
catch
{
// observe each task's, exception
}
var allSecondaries = new List<(int index, List<JObject> related)>();
var exceptionsDict = new Dictionary<int, Exception>();
foreach (var relatedObjectsTask in relatedObjectsTasks)
{
try
{
allSecondaries.Add(relatedObjectsTask.Result);
}
catch (Exception ex)
{
exceptionsDict.Add(relatedObjectsTask.index, ex);
}
}
return ConvertToBulkResponse(allSecondaries, exceptionsDict);
}
You could look into each task's IsFaulted/Exception and IsCancelled properties instead of causing exceptions to be thrown:
foreach (var relatedObjectsTask in relatedObjectsTasks)
{
if (relatedObjectsTask.IsCancelled)
{
exceptionsDict.Add(
relatedObjectsTask.index,
new TaskCancelledException(relatedObjectsTask));
}
else if (relatedObjectsTask.IsFaulted)
{
exceptionsDict.TryAdd(relatedObjectsTask.index, ex);
}
else
{
allSecondaries.Add(relatedObjectsTask.Result);
}
}

async processing in c#

I have a service that implements functionality to return data from sentiment analysis APIs. The client can request results from one or all engines and I want to collate all the data together. I want to process these async and wait for them all to complete before returning the result set. I'm new to async programming and I really cant figure out how to arrange the code and how to implement it syntactically. Here's an EXAMPLE of what I'm TRYING to achieve (I know this doesn't work, but you get the idea; hopefully :-) ):
private ISentimentResponse ProcessRequest(ISentimentRequest request, SentimentEngineServices selectedEngines)
{
SentimentResponse response = new SentimentResponse();
List<Task> taskList = new List<Task>();
foreach (SentimentEngineServices engineService in (SentimentEngineServices[])Enum.GetValues(typeof(SentimentEngineServices)))
{
if (((int)engineService & (int)selectedEngines) > 0)
{
ISentimentEngine engine = _engineFactory.GetSentimentEngine(engineService, null);
Task<ISentimentEngineResult> task = new Task<ISentimentEngineResult>(engine.AnalyseSentimentASync(request));
taskList.Add(task);
}
}
if (taskList.Count > 0)
{
ISentimentEngineResult[] results = Task<ISentimentEngineResult>.WaitAll(taskList);
foreach (result in results)
response.Add(results);
}
return response;
}
The engine has the following code implementation of engine.AnalyseSentimentASync:
public ISentimentEngineResult AnalyseSentiment(ISentimentRequest request)
{
try
{
MultiLanguageBatchInput sentimentList = SentimentRequestToMicrosoftBatchInput(request, Properties.Settings.Default.DefaultLanguage);
SentimentBatchResult sentiment = _client.Sentiment(sentimentList);
KeyPhraseBatchResult keyPhrases = _client.KeyPhrases(sentimentList);
return MicrosoftBatchResultsToSentimentEngineResult(sentiment, keyPhrases);
}
catch (Exception ex)
{
_logger.LogMessage(ex,$"{EngineName} threw an unknown exception: ", LoggingLevel.Error);
throw;
}
}
public async Task<ISentimentEngineResult> AnalyseSentimentAsync(ISentimentRequest request)
{
return AnalyseSentiment(request);
}
What do I need to do and is there any better way to achieve this?
I've looked everywhere for an example but I cant find one that addresses my implementation requirements, or the whole approach is completely wrong!
Thanks all,
Stu.
This is how you can convert it to async:
public async Task<ISentimentEngineResult> AnalyseSentimentAsync(ISentimentRequest request)
{
try
{
MultiLanguageBatchInput sentimentList = SentimentRequestToMicrosoftBatchInput(request, Properties.Settings.Default.DefaultLanguage);
SentimentBatchResult sentiment = await _client.SentimentAsync(sentimentList);
KeyPhraseBatchResult keyPhrases = await _client.KeyPhrasesAsync(sentimentList);
return MicrosoftBatchResultsToSentimentEngineResult(sentiment, keyPhrases);
}
catch (Exception ex)
{
_logger.LogMessage(ex,$"{EngineName} threw an unknown exception: ", LoggingLevel.Error);
throw;
}
}
private async Task<ISentimentResponse> ProcessRequestAsync(ISentimentRequest request, SentimentEngineServices selectedEngines)
{
SentimentResponse response = new SentimentResponse();
List<Task<ISentimentEngineResult>> taskList = new List<Task<ISentimentEngineResult>>();
foreach (SentimentEngineServices engineService in (SentimentEngineServices[])Enum.GetValues(typeof(SentimentEngineServices)))
{
if (((int)engineService & (int)selectedEngines) > 0)
{
ISentimentEngine engine = _engineFactory.GetSentimentEngine(engineService, null);
Task<ISentimentEngineResult> task = engine.AnalyseSentimentASync(request);
taskList.Add(task);
}
}
if (taskList.Count > 0)
{
ISentimentEngineResult[] results = await Task.WhenAll(taskList);
foreach (result in results)
response.Add(results);
}
return response;
}
Remember that you have to call it from some kind of event handler. I don't know what framework you are using (wpf, asp.net, windows service, webapi).
Ok, so here it is:
public ISentimentResponse AnalyseSentiment(ISentimentRequest request, SentimentEngineServices selectedEngines)
{
if (selectedEngines == SentimentEngineServices.None) throw new ArgumentException(nameof(selectedEngines));
ValidateRequest(request);
return ProcessRequestAsync(request, selectedEngines).Result;
}
private async Task<ISentimentResponse> ProcessRequestAsync(ISentimentRequest request, SentimentEngineServices selectedEngines)
{
SentimentResponse response = new SentimentResponse();
List<Task<ISentimentEngineResult>> taskList = new List<Task<ISentimentEngineResult>>();
foreach (SentimentEngineServices engineService in (SentimentEngineServices[])Enum.GetValues(typeof(SentimentEngineServices)))
{
if (((int)engineService & (int)selectedEngines) > 0)
{
ISentimentEngine engine = _engineFactory.GetSentimentEngine(engineService, null);
Task<ISentimentEngineResult> task = engine.AnalyseSentimentASync(request);
taskList.Add(task);
}
}
if (taskList.Count > 0)
{
ISentimentEngineResult[] results = await Task.WhenAll(taskList);
foreach (var result in results)
response.Add(result);
}
return response;
}
And the sentiment interface implementation:
public async Task<ISentimentEngineResult> AnalyseSentiment(ISentimentRequest request)
{
try
{
MultiLanguageBatchInput sentimentList = SentimentRequestToMicrosoftBatchInput(request, Properties.Settings.Default.DefaultLanguage);
SentimentBatchResult sentiment = await _client.SentimentAsync(sentimentList);
KeyPhraseBatchResult keyPhrases = await _client.KeyPhrasesAsync(sentimentList);
return MicrosoftBatchResultsToSentimentEngineResult(sentiment, keyPhrases);
}
catch (Exception ex)
{
_logger.LogMessage(ex,$"{EngineName} threw an unknown exception: ", LoggingLevel.Error);
throw;
}
}
Thanks FCin.

Categories