I am trying calling the async method SendParkInfo method using the await operator like this
await Task.WhenAny(parkList);and await Task.WhenAny(parkInfo);
parkInfo has SendParkInfo method object
Here is some part of my code.
public async Task<AvailableParksResponse> GetAvailableParks(IEnumerable<string> parkRegionList)
{
//
var parkList = parkRegionList.Select(x => SendParkInfo(x)).ToList();
var parkListTask = await Task.WhenAny(parkList);
response.ParkInfoList = new List<Task<ParkInfo>> { parkListTask };
var parkInfo = SendParkInfo(id);
var parkTask = await Task.WhenAny(parkInfo);
response.ParkInfo = new List<Task<ParkInfo>> { parkTask };
//
}
public virtual async Task<ParkInfo> SendParkInfo(string id)
{
//
var apiResponse = await apiClient.GetAsync(RequestUri + id);
//
}
Is it ok to call SendParkInfo like the way I am calling and then using await operator with Task.WhenAny method. Or is there any better way of calling the Async SendParkInfo method
Any help or suggestion would be appreciated.
Thanks in advance
Task.WhenAny() will return a Task which is considered completed when at least one item in the list of tasks passed into WhenAny() has completed. This usually an appropriate option if you want to return data incrementally, as processing completes.
Alternatively, if your intent is to only return a result when all async tasks have completed, consider Task.WhenAll().
Related
I'm trying to fill my model with data that I get from an asynchronous operation to my database. The problem is that the function returns the View (without the completed model), despite my await call.
I have tried to put a timer (I know that is not the solution), to be sure that the problem come from the asynchronous, I have also tried to put on comment some part of code inside my ForEachAsync, but it doesn't seem to help.
I get a list of project, that I fill with some additional information, finally, I assign my object to my model then return the View
public async Task<IActionResult> newProjetList(int GestionaireId, int VilleId)
{
ProjetListModel model = new ProjetListModel();
ProjetService projetService = new ProjetService(connectionString);
UserServices userServices = new UserServices(connectionString);
AvancementService avancementService = new AvancementService(connectionString);
VilleService villeService = new VilleService(connectionString);
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await projets.ToAsyncEnumerable().ForEachAsync(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await p.SousProjet.ToAsyncEnumerable().ForEachAsync(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
//System.Threading.Thread.Sleep(5000);
return View("ProjetList", model);
}
I expected an output with the missing information (here are the 'ville', 'gestionnairesProjet' and 'Avancement'
ForEachAsync only takes an Action<...>, not a Func<..., Task>, so the async lambda your code is passing to ForEachAsync is becoming an async void method. One of the primary reasons async void should be avoided is that it's not easy to determine when the method completes - and in fact, in this case, there is nothing ensuring that it will complete before sending the response.
I recommend doing what Marc suggested and just using foreach:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
foreach (var p in projects)
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
foreach (var sp in p.SousProject)
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
}
}
model.projets = projets;
Or, if you want to use asynchronous concurrency, you can make use of Task.WhenAll:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await Task.WhenAll(projects.Select(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await Task.WhenAll(p.SousProject.Select(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
Use ForEachAwaitAsync instead of ForEachAsync
Explanation: ForEachAsync can't wait since it is simply a multi-threaded execution of your loop (accepts Action). In fact you might have received a compiler warning by using async for your lambda return, because you intend to assign a Task to a void (unused)
ForEachAwaitAsync will wait because it accepts a Func<Task> and internally also awaits / asyncs accordingly.
For curious minds, you can see the source code here: https://github.com/dotnet/reactive/blob/main/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ForEach.cs mentioned as ForEachAwaitAsyncCore and ForEachAsync
My async method is as below:
public async Task<List<object>> handleSummaryOfWallets()
{
string token = giveMeToken("URL AND CREDS");
Channel channel = new Channel("NANANANA GIROUD", ChannelCredentials.Insecure);
OMGadminAPI.OMGadminAPIClient client = new OMGadminAPI.OMGadminAPIClient(channel);
var summaryBalancesParams = new OMGadminAPIGetCurrenciesSummariesParams();
summaryBalancesParams.AdminAuthTokenSecret = token;
List<object> summariesCurrenciesOMGadmin = new List<object>();
using (var call = client.GetCurrenciesSummaries(summaryBalancesParams))
{
while (await call.ResponseStream.MoveNext())
{
OMGadminAPICurrencySummary currencySummary = call.ResponseStream.Current;
summariesCurrenciesOMGadmin.Add(currencySummary);
Console.WriteLine(summariesCurrenciesOMGadmin);
}
return summariesCurrenciesOMGadmin;
}
}
As you can see, above async method returns list of objects. I call this method as below:
var listOfBalances = balances.handleSummaryOfWallets().Wait();
and it gives me error:
Error CS0815: Cannot assign void to an implicitly-typed variable
From the error, I understand that this is not correct way to call async method. But I need to read ready list of objects from async fetched data. Its request-response, no real stable stream. So I need to generate this list only once per request. I'm using gRPC framework for RPC calls.
Please help me fetch this data and make ready to use.
The Task.Wait method waits for the Task to complete execution. It returns void. That is the reason why the exception.
Now to overcome the exception and to read the return value, one way is as mentioned in other answer and the comments; await the call as below:
public async void TestAsync()
{
var listOfBalances = await handleSummaryOfWallets();
}
Note that your calling method should also be async method now.
As you are calling Wait in your code, it looks that you want the result immediately; you have nothing else left to do that does not depend on result. In that case, you may choose to stop async chain by calling Wait. But you need to do some changes as below:
public void TestAsync()
{
var task = handleSummaryOfWallets();//Just call the method which will return the Task<List<object>>.
task.Wait();//Call Wait on the task. This will hold the execution until complete execution is done.
var listOfBalances = task.Result;//Task is executed completely. Read the result.
}
Note that calling method is no longer async. Other explanation is given in code-comments.
Other short alternative to above code is as below:
public void TestAsync()
{
var listOfBalances = handleSummaryOfWallets().Result;
}
Just use await while calling your method
var listOfBalances = await balances.handleSummaryOfWallets();
I am a new in the Async/Await functionality and I tried to use them in my MVC project.
So from the Controller I call the current method to initialize my model:
var model = this.quantService.GetIndexViewModel(companyIds, isMore, currentrole).Result;
In this GetIndexViewModel I use await:
public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
{
return new CompanyBoardViewModel
{
TopJoinAplicant = await this.TopJointApplicant(parameter, isMore),
TopPriorityCountry = await this.TopPriorityCountry(parameter),
TopPublicationCountries = await this.TopPublicationCountries(parameter),
TopGrantedInventors = await this.TopGrantedInventors(parameter),
TopIPC = await this.TopIPC(parameter),
TopCPC = await this.TopCPC(parameter),
TopCitedInventors = await this.TopCitedInventors(parameter),
TopCitedPatents = await this.TopCitedPatents(parameter),
CGAR = await this.GetCGAR(parameter),
};
}
For the first method I use these code:
private async Task<QuantTableViewModel<TopFilterViewModel>> TopJointApplicant(IEnumerable<int> ids, bool isMore = false)
{
return await Task.Run(() => new QuantTableViewModel<TopFilterViewModel>
{
Tableid = "TopJointApplicants",
Title = "Top Joint Applicants",
FirstCol = "Position",
SecondCol = "Joint Applicant",
ThirdCol = "#",
IsSeeMore = isMore,
Data = this.cache.TopJointApplicant(ids).ToList()
});
}
In this method I call : Data = this.cache.TopJointApplicant(ids).ToList()
this method created a procedure and get information from the Database(the method is executed without any problems), but when I try to return the QuantTableViewModel<TopFilterViewModel> I stack(as I go in a death log).
I will be really happy if anyone know why this is happened.
I explain the deadlock you're seeing on my blog. In short, don't block on async code; instead, use async all the way.
But there are other problems with your approach. As others have noted, await Task.Run is an antipattern on ASP.NET. You may want to read my article on async ASP.NET.
Finally, one other tip: you're approaching the problem from the wrong direction. Instead of just choosing a method to "make async", you should first think about what your application is doing, and start converting I/O calls to async at the lowest level. Convert them to use async APIs instead of blocking APIs (i.e., no Task.Run). Then change their callers to async, and their callers to async, eventually changing your controller method(s) to async.
You don't need to use Task.Run when using the async/await pattern.
You can actually have an async controller so you don't need to call .Result which renders async operation to run synchronously.
something like:
public Task<ActionResult> Index(object parameter)
{
var model = await this.quantService.GetIndexViewModel(companyIds, isMore, currentRole);
return View(model);
}
In this case I would say that's it's enough that your public method is async, since there's not really any asyncronous going on in TopJointApplicant.
public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
{
return new CompanyBoardViewModel
{
TopJoinAplicant = this.TopJointApplicant(parameter, isMore),
TopPriorityCountry = await this.TopPriorityCountry(parameter),
TopPublicationCountries = await this.TopPublicationCountries(parameter),
TopGrantedInventors = await this.TopGrantedInventors(parameter),
TopIPC = await this.TopIPC(parameter),
TopCPC = await this.TopCPC(parameter),
TopCitedInventors = await this.TopCitedInventors(parameter),
TopCitedPatents = await this.TopCitedPatents(parameter),
CGAR = await this.GetCGAR(parameter),
};
}
private QuantTableViewModel<TopFilterViewModel> TopJointApplicant(IEnumerable<int> ids, bool isMore = false)
{
var Data = this.cache.TopJointApplicant(ids).ToList();
return new QuantTableViewModel<TopFilterViewModel>
{
Tableid = "TopJointApplicants",
Title = "Top Joint Applicants",
FirstCol = "Position",
SecondCol = "Joint Applicant",
ThirdCol = "#",
IsSeeMore = isMore,
Data = this.cache.TopJointApplicant(ids).ToList()
});
}
I recommend you to fully embrace the await/async-pattern if you're going to use it. This means that your controller also should use await/async.
public class YourController : Controller
{
// Note the Task<ActionResult> and async in your controller.
public async Task<ActionResult> YourControllerMethod()
{
var model = await this.quantService.GetIndexViewModel(companyIds, isMore, currentrole);
return View(model); // Or something like this.
}
}
Also, consider your naming convention. For clarity, async methods should end with the suffix Async, such as GetIndexViewModelAsync.
EDIT:
Based on the comments I think I should clarify what await/async does. An operation will not execute faster simply because you use the await/async-pattern, rather the opposite. Async/await creates an overhead for the thread management which would likely cause your operation to execute slower.
Instead, there are 2 main advantages:
When using async/await you will not block the thread. This means that while you're application is waiting for something else (such as IO, DB or webservice call) the thread can be used for something else, such as executing another we request. This doesn't mean that an DB-call will be executed faster. But it will let the thread do something else while waiting. If you're using IIS the number of threads are limited. So instead of locking them with expensive IO, they can serve another request while waiting.
You may do more things at the same time. For example, you could send a request to you DB, while executing a slow webservice call at the same time. This may cause the total execution time to be faster, since you're doing more things at the same time. However, there are limitations. For instance, if you're using Entity Framework, only one thread may access the context at the time. But while waiting for the DB, you can do something else. For example:
public class MyThreadingClass
{
private Task ExecuteWebServiceCallAsync()
{
return await _myService.DoSomething();
}
private Task ExecuteDbQueryAsync()
{
return await _context.Customer.FirstOrDefaultAsync();
}
public void DoThingsWithWaitAll()
{
var tasks = new Task[2];
// Fire up first task.
tasks[0] = ExecuteWebServiceCallAsync();
// Fire up next task.
tasks[1] = ExecuteDbQueryAsync();
// Wait for all tasks.
Task.WaitAll(tasks);
}
public Task DoThingsWithWithAwaitAsync()
{
// Fire up first task.
var webServiceTask = ExecuteWebServiceCallAsync();
// Fire up next task.
var dbTask = ExecuteDbQueryAsync();
// Wait for all tasks.
await webServiceTask;
await dbTask;
}
}
So, to sum up. The reason why you should use await/async is when you can do it ALL THE WAY down to the execution of the slow operation (such as DB or webservice). Or if you wish to do several things at once.
In your particular case you can do something like this:
public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
{
// Let the threads start processing.
var topApplicantTask = this.TopJointApplicant(parameter, isMore);
var topPriorityCountryTask = this.TopPriorityCountry(parameter);
var topPublicationContriesTask = this.TopPublicationCountries(parameter);
var topIPCTask = this.TopIPC(parameter);
var topCPCTask = this.TopCPC(parameter);
var topCitedInventorsTask = this.TopCitedInventors(parameter);
var topCitetPatentsTask = this.TopCitedPatents(parameter);
var getCGARTask = this.GetCGAR(parameter);
// Await them later.
return new CompanyBoardViewModel
{
TopJoinAplicant = await topApplicantTask,
TopPriorityCountry = await topPriorityCountryTask,
TopPublicationCountries = await topPublicationContriesTask,
TopGrantedInventors = await this.TopGrantedInventors(parameter),
TopIPC = await topIPCTask,
TopCPC = await topCPCTask,
TopCitedInventors = await topCitedInventorsTask,
TopCitedPatents = await topCitetPatentsTask,
CGAR = await getCGARTask,
};
}
But try to avoid Task.Run since it's considered an anti-pattern. Instead, try to use await/async all the way from the controller to the actual operation (DB, IO, webservice). Also, in the example above there's a lot of threading going on. It should likely be cleaned up a bit, but you can see it as a proof-of-concept more than the suggested solution.
This question already has answers here:
Nesting await in Parallel.ForEach [duplicate]
(11 answers)
Closed last year.
I had such method:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
foreach(var method in Methods)
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}
return result;
}
Then I decided to use Parallel.ForEach:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
Parallel.ForEach(Methods, async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
});
return result;
}
But now I've got an error:
An asynchronous module or handler completed while an asynchronous operation was still pending.
async doesn't work well with ForEach. In particular, your async lambda is being converted to an async void method. There are a number of reasons to avoid async void (as I describe in an MSDN article); one of them is that you can't easily detect when the async lambda has completed. ASP.NET will see your code return without completing the async void method and (appropriately) throw an exception.
What you probably want to do is process the data concurrently, just not in parallel. Parallel code should almost never be used on ASP.NET. Here's what the code would look like with asynchronous concurrent processing:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
var tasks = Methods.Select(method => ProcessAsync(method)).ToArray();
string[] json = await Task.WhenAll(tasks);
result.Prop1 = PopulateProp1(json[0]);
...
return result;
}
.NET 6 finally added Parallel.ForEachAsync, a way to schedule asynchronous work that allows you to control the degree of parallelism:
var urlsToDownload = new []
{
"https://dotnet.microsoft.com",
"https://www.microsoft.com",
"https://twitter.com/shahabfar"
};
var client = new HttpClient();
var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
await Parallel.ForEachAsync(urlsToDownload, options, async (url, token) =>
{
var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);
var response = await client.GetAsync(url, token);
// The request will be canceled in case of an error in another URL.
if (response.IsSuccessStatusCode)
{
using var target = File.OpenWrite(targetPath);
await response.Content.CopyToAsync(target);
}
});
Alternatively, with the AsyncEnumerator NuGet Package you can do this:
using System.Collections.Async;
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
await Methods.ParallelForEachAsync(async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}, maxDegreeOfParallelism: 10);
return result;
}
where ParallelForEachAsync is an extension method.
Ahh, okay. I think I know what's going on now. async method => an "async void" which is "fire and forget" (not recommended for anything other than event handlers). This means the caller cannot know when it is completed... So, GetResult returns while the operation is still running. Although the technical details of my first answer are incorrect, the result is the same here: that GetResult is returning while the operations started by ForEach are still running. The only thing you could really do is not await on Process (so that the lambda is no longer async) and wait for Process to complete each iteration. But, that will use at least one thread pool thread to do that and thus stress the pool slightly--likely making use of ForEach pointless. I would simply not use Parallel.ForEach...
During startup, the application I need to get two sets of data, each has its asynchronous method. If I call them one by one, then the second call will pass only after completion of the first.
List<DataOne> DataCollectionOne;
List<DataTwo> DataCollectionTwo;
async void GetDatas()
{
if(sameCondOne)
DataCollectionOne = await GetDataOne();
if(sameCondTwo)
DataCollectionTwo = await GetDataTwo();
}
So I wrapped them in the task calls.
void GetDatas()
{
if(sameCondOne)
Task.Run(() => RunDataOne());
if(sameCondTwo)
Task.Run(() => RunDataTwo());
}
async void RunDataOne()
{
DataCollectionOne = await GetDataOne();
}
async void RunDataTwo()
{
DataCollectionTwo = await GetDataTwo();
}
I am doing right?
No. You don't need, nor want to spin up a new thread just to be responsible for starting these two asynchronous operations. Simply start both operations (calling the method is what starts the operation) and don't await either until you've started them both:
var firstTask = GetDataOne();
var secondTask = GetDataTwo();
var firstResult = await firstTask;
var secondResult = await secondTask;
To handle the conditional check just conditionally start the task, and then conditionally assign the result:
Task<T> firstTask = null;
if(shouldGetFirstTask)
firstTask = GetDataOne();
Task<T> secondTask = null;
if(shouldGetSecondTask)
secondTask = GetDataTwo();
if(firstTask != null)
DataCollectionOne = await firstTask;
if(secondTask != null)
DataCollectionTwo = await secondTask;
First of all, you should avoid void returning async methods (Best Practices in Asynchronous Programming).
It is a common practice to suffix async methods with Async (or TaskAsync if Async suffixed methods already exist).
If I understand you correctly, you want to spawn some conditional asynchronous interdependent tasks and wait for all the tasks to end without having any of them wait for another.
Since you are doing anything with the result of the tasks in the orchestrating method but you need istead some side effects, I would wrap the Get~ methods into Run~ methods like you did.
You could test the condition inside the Run~ methods (either by explicitly testing the condition or having it as a parameter):
async Task RunDataOneAsymc()
{
if (sameCondOne)
{
DataCollectionOne = await GetDataOneAsync();
}
}
But this would still spawn unnecessary tasks if the condition is false. So, it's better to keep it on the caller side:
async Task RunDataOneAsymc()
{
DataCollectionOne = await GetDataOneAsync();
}
// ...
if (sameCondOne)
{
await RunDataOneAsymc();
}
On the orchestating method, you will spawn the tasks if the condition is true and then wait for all of them to complete:
async Task RunAll()
{
var tasks = new List<Task>();
if (sameCondOne)
{
await RunDataOneAsymc();
}
// ...
if (sameCondN)
{
await RunDataNAsymc();
}
await Task.WhenAll(tasks);
}