I need to run multiple task to access to database and to wait to all task finished to obtain the result and assign it to my viewmodel, i try many sample but i have never have the result, any help please, this my code
var model = new AggregationViewModel();
var loadDataTasks = new Task[]
{
Task.Run(async () =>model.Alive = await _repository.GetCountAsync<filter>(Predicate)),
Task.Run(async () => model.Delay = await _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")))
};
try
{
await Task.WhenAll(loadDataTasks);
foreach (var item in loadDataTasks)
{
}
}
catch (Exception ex)
{
}
Check this out:
var task1 = _repository.GetCountAsync<filter>(Predicate);
var task2 = _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused"));
await Task.WhenAll(task1, task2); //wait both tasks to finish
model.Alive = await task1;
model.Delay = await task2;
PS: the function, which the above code is placed, is needed to be async for the above scenario.
Try this code:
var model = new AggregationViewModel();
//----------------------------------------
var tasks = new List<Task<int>>();
//Add CountA
tasks.Add(_repository.GetCountAsync<filter>(Predicate));
//Add CountB
tasks.Add(_repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")));
//----------------------------------------
// Create a task with "allTasks" name to wait to complete all tasks
Task allTasks = Task.WhenAll(tasks);
try {
// Wait to compelete "allTask"
allTasks.Wait();
}
catch(AggregateException)
{}
//----------------------------------------
//Working on the results
if (allTasks.Status == TaskStatus.RanToCompletion) {
model.CountA=allTasks.Result[0]
model.CountB=allTasks.Result[1]
}
// Display information on faulted tasks.
else {
foreach (var t in tasks) {
Console.WriteLine("Task {0}: {1}", t.Id, t.Status);
}
}
Related
I would like to run several methods asyncron in a foreach. The return value should be written to a list.
The method is executed in a WPF application. The method GetItemPricesFromJsonAsync fetches from the web data.
public async Task LoadBlackMarketListView(List<MarketAnalysisManager.ItemTier> tiers, List<MarketAnalysisManager.ItemLevel> levels,
List<MarketAnalysisManager.ItemQuality> quialityList, string outdatedHours, string profit, Location? location)
{
await Task.Run(async () =>
{
var blackMarketSellObjectList = new List<BlackMarketSellObject>();
var items = await MarketAnalysisManager.GetItemListAsync(tiers, levels);
await Dispatcher.InvokeAsync(() =>
{
PbBlackMarketMode.Minimum = 0;
PbBlackMarketMode.Maximum = items.Count;
PbBlackMarketMode.Value = 0;
GridBlackMarketMode.IsEnabled = false;
LvBlackMarket.Visibility = Visibility.Hidden;
PbBlackMarketMode.Visibility = Visibility.Visible;
});
foreach (var item in items)
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
continue;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}
await Dispatcher.InvokeAsync(() =>
{
LvBlackMarket.ItemsSource = blackMarketSellObjectList;
PbBlackMarketMode.Visibility = Visibility.Hidden;
LvBlackMarket.Visibility = Visibility.Visible;
GridBlackMarketMode.IsEnabled = true;
});
});
}
Currently it looks like he's only doing one thing at a time.
Run... 0
End... 0
Run... 1
End... 1
Run... 2
End... 2
You will need to store the Tasks, not await them. Then you can wait for all of them.
Try this (replace your foreach with my code).
I would also advise you to use a real method instead of the annonymous one, it's much more readable.
List<Task> tasks = new List<Task>();
foreach (var item in items)
{
tasks.Add(Task.Run(async () =>
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
return;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}));
}
await Task.WhenAll(tasks);
Note: There is now a return instead of a continue since this is an annonymous function and you just have to end the function there instead of continuing with the foreach.
The following code is used to simulate the time-consuming works.
async Task DoWork(string n)
{
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
}
async Task<int> F1()
{
await DoWork("f1");
return 1;
}
async Task<string> F2()
{
await DoWork("f2");
return "X";
}
The following code will run F1() and F2() sequentially.
async Task<string> Main()
{
var f1Task = F1();
var f2Task = F2();
var f1 = await f1Task;
var f2 = await f2Task;
return $"{f1} {f2}";
}
await Main();
The following code can make them run in parallel. However, it looks cumbersome.
async Task<string> Main2()
{
int f1 = 0;
async Task G1() { f1 = await F1(); }
string f2 = "";
async Task G2() { f2 = await F2(); }
await Task.WhenAll(Task.Run(() => G1()), Task.Run(() => G2()));
return $"{f1} {f2}";
}
await Main2();
Is there a way to do it without wrapping them in Task.Run()?
You can use WhenAll() method to have your task run parallel like
async Task<int> Main()
{
var f1Task = F1();
var f2Task = F2();
await Task.WhenAll(f1Task,f2Task);
return await f1Task + await f2Task;
}
await itself doesn't start the asynchronous operation.
Kick off DoWork with a Task like this:
async Task DoWork(string n)
{
await Task.Run(() => {
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
});
}
Then you cando this:
{
var f1Task = F1();
var f2Task = F2();
return await f1Task + await f2Task;
}
You're right that it looks cumbersome. However, it can be done in a neat way. You can create a tasks array and then use WhenAll to wait for all of them at once.
var tasks = new List<Task<int>> { F1(), F2() };
var result = Task.WhenAll(tasks).GetAwaiter().GetResult();
The only condition is that all tasks has to return the same result type. Then result in this case is array of int.
I wrote a nice article about parallel processing, you can have a look at how this can be accomplished when a task is a REST call: http://www.michalbialecki.com/2018/04/19/how-to-send-many-requests-in-parallel-in-asp-net-core/
It will be both io and CPU heavy.
If you have just I/O (i.e., await Task.Delay instead of Thread.Sleep), then you can use asynchronous concurrency:
var task1 = F1();
var task2 = F2();
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
However, since you have CPU as well (and since your method is synchronous), you'll need to push that to another thread and make it parallel.
Is there a way to do it without wrapping them in Task.Run()?
Yes, but Task.Run is the simplest solution here. It can look less cumbersome than your example:
var task1 = Task.Run(() => F1());
var task2 = Task.Run(() => F2());
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
Technically, the Task.WhenAll is optional in both of these examples, but I like it since it makes the semantics more clear.
I have the following test code to simulate the use of a semaphore and throttling task execution. Is there a way to not continue to create new tasks if one of the running tasks throws an exception like the below. I don't need the existing tasks to stop running, I just want no new tasks to start after the exception is encountered.
Currently the tasks will all start in this scenario below. I want it to stop after a few tasks are ran because of the exceptions being thrown.
var testStrings = new List<string>();
for (var i = 0; i < 5000; i++)
{
testStrings.Add($"string-{i}");
}
using (var semaphore = new SemaphoreSlim(10))
{
var tasks = testStrings.Select(async testString =>
{
await semaphore.WaitAsync();
try
{
Console.WriteLine($"{testString}-Start");
await Task.Delay(2000);
throw new Exception("test");
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
}
As per Sinatr's comments, I think this may work for me by adding a cancellation token to be monitored.
var testStrings = new List<string>();
for (var i = 0; i < 5000; i++)
{
testStrings.Add($"string-{i}");
}
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
using (var semaphore = new SemaphoreSlim(10))
{
var tasks = testStrings.Select(async testString =>
{
await semaphore.WaitAsync();
try
{
if (!cancellationToken.IsCancellationRequested)
{
Console.WriteLine($"{testString}-Start");
await Task.Delay(2000);
throw new Exception("test");
}
}
catch (Exception ex)
{
if (!cancellationTokenSource.IsCancellationRequested)
cancellationTokenSource.Cancel();
throw;
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
}
Let's say I have more Uri's. I need to validate, if they are reachable.
public RelayCommand TestConnectionCommand => new RelayCommand(async () =>
{
var res1 = await ValidateUriAsync(uri);
var res2 = await ValidateUriAsync(uri);
});
private async Task<bool> ValidateUriAsync(Uri uri)
{
try
{
var request = WebRequest.CreateHttp(uri);
var result = await request.GetResponseAsync();
return true;
}
catch (Exception e)
{
return false;
}
}
When the program comes to first await it takes some time to validate the uri, after I get the result, I can show the result on the View. Then program goes next and I validate second uri. I'd like to do that parallel, without awaiting. I was thinking about using Begin/EndGetResponse. I need to show the result for each validation on the View. Validation succeeded/failed.
Many thanks for advice.
When using await you stop the execution until the task returns, instead wait for all task to finish:
var task1 = ValidateUriAsync(uri);
var task2 = ValidateUriAsync(uri);
await Task.WhenAll(task1, task2);
or to wait until the first fault:
var tasks = new List<Task>
{
ValidateUriAsync(), ValidateUriAsync(uri)
};
while (tasks.Any())
{
var t = await Task.WhenAny(tasks);
if (t.IsFaulted)
{
//Faulty
break;
}
tasks.Remove(t);
}
My code is an async api call, and looks like the example below
public async Task<IEnumerable<TaskObject>> GetTaskObjects()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
tasks.Add(shizzle.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
tasks.Add(shizzle2.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
await Task.WhenAll(tasks);
return tasks.Select(x => x.Result).ToList();
}
The controller is a Stateless Service Fiber Web Api that makes some calls to a statefull service. Is this a good solution? Are there beter ones? Are the async and await keywords even necessary if this is an api call?
public async Task<IEnumerable<TaskObject>> GetTaskObjects2()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
//Add your task to the collection
tasks.Add(shizzle);
tasks.Add(shizzle2);
//wait for when all task are finished and it will return the data.
return await Task.WhenAll(tasks);
}
If this line of codes are really awaitable
/// At this point, all two tasks are running at the same time.
var shizzle = DoShizzleAsync();
var shizzle2 = DoShizzle2Async();
await Task.WhenAll(shizzle2, shizzle);
See Stephen Cleary blog for more information