Task.WhenAny ContinueWith: Get argument? - c#

I want to execute a list of tasks, and perform a synchronous action once any of them is completed, but I need to know which of them it was.
See my example, and look out for the comment in the code, that precedes a couple of lines I don't know how to achieve.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
public async Task GreetAllAsync()
{
var tasks = UserNames.Select(un => GreetAsync(un)).ToList();
while(tasks.Any())
{
var finished = await Task.WhenAny(tasks);
if(finished.Result)
{
//Here's what I'd like to achieve
var username = finished.Arguments[0];
WriteLine($"User {username} has been greeted.");
}
tasks.Remove(finished);
}
}
Based on this example.
In my real world scenario, I have a list of customers, which I have to walk thru them one by one and update a remote server on their credit status (the remote server doesn't support batch updates). After each of them has been updated, I have to mark in my database, that this customer has been accredited.

You almost never want to actually process a list of tasks one at a time as they complete like that. Instead, just introduce a higher-level operation and rewrite your Task.WhenAny to a Task.WhenAll to wait for those higher-level operations.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
private async Task<bool> GreetAndReportGreetedAsync(string name)
{
var result = await GreetAsync(name);
WriteLine($"User {name} has been greeted.");
return result;
}
public async Task GreetAllAsync()
{
await Task.WhenAll(UserNames.Select(un => GreetAsync(un));
}

Why not simply use ContinueWith? Something like this:
public async Task GreetAllAsync(List<string> UserNames)
{
var tasks = UserNames
.Select(un => GreetAsync(un)
.ContinueWith(x => {
Console.WriteLine(un + " has been greeted");
}));
await Task.WhenAll(tasks);
}

Related

Correct pattern to check if an async Task completed synchronously before awaiting it

I have a bunch of requests to process, some of which may complete synchronously.
I'd like to gather all results that are immediately available and return them early, while waiting for the rest.
Roughly like this:
List<Task<Result>> tasks = new ();
List<Result> results = new ();
foreach (var request in myRequests) {
var task = request.ProcessAsync();
if (task.IsCompleted)
results.Add(task.Result); // or Add(await task) ?
else
tasks.Add(task);
}
// send results that are available "immediately" while waiting for the rest
if (results.Count > 0) SendResults(results);
results = await Task.WhenAll(tasks);
SendResults(results);
I'm not sure whether relying on IsCompleted might be a bad idea; could there be situations where its result cannot be trusted, or where it may change back to false again, etc.?
Similarly, could it be dangerous to use task.Result even after checking IsCompleted, should one always prefer await task? What if were using ValueTask instead of Task?
I'm not sure whether relying on IsCompleted might be a bad idea; could there be situations where its result cannot be trusted...
If you're in a multithreaded context, it's possible that IsCompleted could return false at the moment when you check on it, but it completes immediately thereafter. In cases like the code you're using, the cost of this happening would be very low, so I wouldn't worry about it.
or where it may change back to false again, etc.?
No, once a Task completes, it cannot uncomplete.
could it be dangerous to use task.Result even after checking IsCompleted.
Nope, that should always be safe.
should one always prefer await task?
await is a great default when you don't have a specific reason to do something else, but there are a variety of use cases where other patterns might be useful. The use case you've highlighted is a good example, where you want to return the results of finished tasks without awaiting all of them.
As Stephen Cleary mentioned in a comment below, it may still be worthwhile to use await to maintain expected exception behavior. You might consider doing something more like this:
var requestsByIsCompleted = myRequests.ToLookup(r => r.IsCompleted);
// send results that are available "immediately" while waiting for the rest
SendResults(await Task.WhenAll(requestsByIsCompleted[true]));
SendResults(await Task.WhenAll(requestsByIsCompleted[false]));
What if were using ValueTask instead of Task?
The answers above apply equally to both types.
You could use code like this to continually send the results of completed tasks while waiting on others to complete.
foreach (var request in myRequests)
{
tasks.Add(request.ProcessAsync());
}
// wait for at least one task to be complete, then send all available results
while (tasks.Count > 0)
{
// wait for at least one task to complete
Task.WaitAny(tasks.ToArray());
// send results for each completed task
var completedTasks = tasks.Where(t => t.IsCompleted);
var results = completedTasks.Where(t => t.IsCompletedSuccessfully).Select(t => t.Result).ToList();
SendResults(results);
// TODO: handle completed but failed tasks here
// remove completed tasks from the tasks list and keep waiting
tasks.RemoveAll(t => completedTasks.Contains(t));
}
Using only await you can achieve the desired behavior:
async Task ProcessAsync(MyRequest request, Sender sender)
{
var result = await request.ProcessAsync();
await sender.SendAsync(result);
}
...
async Task ProcessAll()
{
var tasks = new List<Task>();
foreach(var request in requests)
{
var task = ProcessAsync(request, sender);
// Dont await until all requests are queued up
tasks.Add(task);
}
// Await on all outstanding requests
await Task.WhenAll(tasks);
}
There are already good answers, but in addition of them here is my suggestion too, on how to handle multiple tasks and process each task differently, maybe it will suit your needs. My example is with events, but you can replace them with some kind of state management that fits your needs.
public interface IRequestHandler
{
event Func<object, Task> Ready;
Task ProcessAsync();
}
public class RequestHandler : IRequestHandler
{
// Hier where you wraps your request:
// private object request;
private readonly int value;
public RequestHandler(int value)
=> this.value = value;
public event Func<object, Task> Ready;
public async Task ProcessAsync()
{
await Task.Delay(1000 * this.value);
// Hier where you calls:
// var result = await request.ProcessAsync();
//... then do something over the result or wrap the call in try catch for example
var result = $"RequestHandler {this.value} - [{DateTime.Now.ToLongTimeString()}]";
if (this.Ready is not null)
{
// If result passes send the result to all subscribers
await this.Ready.Invoke($"RequestHandler {this.value} - [{DateTime.Now.ToLongTimeString()}]");
}
}
}
static void Main()
{
var a = new RequestHandler(1);
a.Ready += PrintAsync;
var b = new RequestHandler(2);
b.Ready += PrintAsync;
var c = new RequestHandler(3);
c.Ready += PrintAsync;
var d= new RequestHandler(4);
d.Ready += PrintAsync;
var e = new RequestHandler(5);
e.Ready += PrintAsync;
var f = new RequestHandler(6);
f.Ready += PrintAsync;
var requests = new List<IRequestHandler>()
{
a, b, c, d, e, f
};
var tasks = requests
.Select(x => Task.Run(x.ProcessAsync));
// Hier you must await all of the tasks
Task
.Run(async () => await Task.WhenAll(tasks))
.Wait();
}
static Task PrintAsync(object output)
{
Console.WriteLine(output);
return Task.CompletedTask;
}

Get result from Task.Run() function without having to the caller method to wait for it?

Is there a way to get the result of a Task.Run function like this
private Task<bool> UpdteSth(double bla)
=> Task.Run(() =>
{
try
{
UpdateOneAsync(doStuff);
}
catch (Exception exception)
{
sentryClient.CaptureException(exception);
return false;
}
return true;
});
from a caller method like this
public async Task<Entity> Get()
{
UpdateSth(double bla);
}
without having to await UpdateSth so Get can freely do whatever it needs without having to wait to UpdateSth result? I still need to get the result of UpdateSth() to do some logging business but other than that Get should not wait for UpdateSth to be done to continue its job.
Clearly, await and Task.FromResult() still make my Get() to wait for the result or UpdateSth to be done so I cannot used those.
I'm not getting the issue here. If you want to fire-and-forget you just call UpdateSth(bla);. What am I missing here?
Here's an example of what that looks like:
public Task<Entity> Get() =>
Task.Run(() =>
{
double bla = 4.2;
UpdateSth(bla);
return new Entity();
});
This is a trivial example. I feel that there is a more stringent requirement that hasn't been explained in the question.

Is my code executed in the correct order?

I have the following code and I want that it is executed in the correct order, but I'm not sure if I use "await" correctly so that it is executed in the correct order.
The correct sequence should be:
1)Call GetTiltleData and get the CurrentCatalogVersion.
2)Call GetCatalogData to get the ItemID of the item that will get purchased.
3)Call MakePurchase to purchase the item.
4)Call GetInventoryList to get the player's current(after the purchase) inventory.
Am I using await correctly? Is my code executed in the correct order or is it possible that the code could be executed in a wrong order?
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
GetTiltleData();
GetCatalogData();
public async void GetTiltleData()
{
await ClientGetTitleData();
}
private async Task ClientGetTitleData()
{
var result = await PlayFabClientAPI.GetTitleDataAsync(new GetTitleDataRequest());
if (result.Result.Data == null || !result.Result.Data.ContainsKey("Catalogversion"))
Console.WriteLine(result.Error.GenerateErrorReport());
else
CurrentCatalogVersion = result.Result.Data["Catalogversion"];
}
public async void GetCatalogData()
{
await GetCatalog();
}
private async Task GetCatalog()
{
var result = await PlayFabClientAPI.GetCatalogItemsAsync(new GetCatalogItemsRequest()
{
CatalogVersion = CurrentCatalogVersion
});
foreach (var entry in result.Result.Catalog)
{
//For example, if you want to purchase a sword
if (entry.DisplayName == "Sword")
ItemID = entry.ItemId;
}
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Listed items successful!");
await MakePurchase(ItemID);
}
}
private async Task MakePurchase(string itemid)
{
var result = await PlayFabClientAPI.PurchaseItemAsync(new PurchaseItemRequest()
{
CatalogVersion = CurrentCatalogVersion,
ItemId = ItemID,
Price = 100,
VirtualCurrency = "GO"
});
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Purchase successful!");
await GetInventoryList();
}
}
private async Task GetInventoryList()
{
var result = await PlayFabClientAPI.GetUserInventoryAsync(new GetUserInventoryRequest());
//Get the current amount of the player's virtual currency after the purchase
CurrentAmount = result.Result.VirtualCurrency["GO"];
foreach (var entry in result.Result.Inventory)
{
//Get a list with the player's items after the purchase
Console.WriteLine($"{entry.DisplayName} {entry.UnitPrice} {entry.ItemId} {entry.ItemInstanceId}");
...
}
if (result.Error != null)
{
// Handle error if any
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Got current inventory");
}
}
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
Yes, absolutely.
This is your calling code:
...
GetTiltleData();
GetCatalogData();
These are asynchronous methods, but you're not awaiting their results before continuing to the next instruction. This means both methods are fire and forget threads.
This means you have a race condition. Literally, you have 2 threads racing to finish their methods before the other. You have no guarantee which one will finish first, or how the CPU will prioritise their instructions. Because of how you've called those methods, you've essentially told the computer you don't care about the outcome of events.
Fastest way to ensure one method completes before the next one starts is to await them both. As Igor says, if you are going to use async methods, you should use async/await the entire way up and down your call stack.
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
await GetTiltleData(); // adding "await" to these 2 lines
await GetCatalogData();
In addition, PLEASE don't use async void in your method signatures. You should use async Task instead. See async/await - when to return a Task vs void?.

Basics of returning a task in from Async Method

Please bear with me if it is too obvious! I am not able to differentiate between these two versions.
In first version I am awaiting a thread to complete I/O operation assign the result to a local variable & return the task.
async public Task<int> GetUpdateFromManager(string name)
{
int newSalary = 0;
await Task.Run(() =>
{
PayrollDB db = new PayrollDB();
newSalary = db.Employees.Where(emp => emp.Name == name).FirstOrDefault().Salary;
});
return newSalary;
}
Here I await on the return from a task.
async public Task<int> GetUpdateFromManager(string name)
{
return await Task.Run(() =>
{
PayrollDB db = new PayrollDB();
return db.Employees.Where(emp => emp.Name == name).FirstOrDefault().Salary;
});
}
Would they always work same. in case of positive results & when database call thwows exception?
Yes, they work the same. But there are a couple of problems with them.
1) Don't use Task.Run to implement asynchronous methods. Instead, use the asynchronous queries available in EF6 and other database APIs.
2) End your method name with Async to follow the TAP guidelines.

How to process tasks as they complete -but each task requires different method to process task's result

I'm using async/await to call few external APIs. All of them returns me a string value but in different format and requires their own processing.
And I want to process the returned value as a task completes. I don't want to wait until all are completed and hence I'm using Task.WhenAny(). How can I process tasks as they complete and still use the correct "Process" method for each task as they complete?
I make some changes after my first post and here is the latest i have:
public async Task<List<string>> Get()
{
var task1 = Method1Async();
var task2 = Method1Async();
var tasks = new List<Task<string>> {task1, task2};
var results = new List<string>();
while (tasks.Count > 0)
{
var justCompletedTask = await Task.WhenAny(tasks);//will not throw
tasks.Remove(justCompletedTask);
try
{
var result = await justCompletedTask;
results.Add(result);
}
catch(Exception)
{
//deal with it
}
}
return results;
}
private async Task<string> Method1Async()
{
//this may throw - something like forbidden or any other exception
var task = _httpClient.GetStringAsync("api1's url here");
var result = await Method1ResultProcessorAsync(task);
return result;
}
private async Task<string> Method1ResultProcessorAsync(Task<string> task)
{
//process task's result -if it successuflly completed and return that
return await task; //for now
}
private async Task<string> Method2Async()
{
//this may throw - something like forbidden or any other exception
var task = _httpClient.GetStringAsync("api2's url here");
var result = await Method2ResultProcessorAsync(task);
return await task;
}
private async Task<string> Method2ResultProcessorAsync(Task<string> task)
{
//This processing logic is entirely different from Method1ResultProcessor
//process task's result -if it successfully completed and return that
return await task; //for now
}
I have two questions here:
Is this the right way to approach the problem?
How do i better handle exception here? This is very important so the failure of one should not fail the whole thing. As long as any of the methods succeed, it will be okay. But if all fails, I want to the Get method to throw.
Since your processor methods already accept Task, you can just call them and they will asynchronously wait for their corresponding results:
public Task<string[]> Get()
{
var task1 = Method1ResultProcessorAsync(Method1Async());
var task2 = Method2ResultProcessorAsync(Method2Async());
return Task.WhenAll(task1, task2);
}
Handling exceptions the way you describe will make this more complicated, but you can use something like:
public async Task<List<string>> Get()
{
var task1 = Method1ResultProcessorAsync(Method1Async());
var task2 = Method2ResultProcessorAsync(Method2Async());
var tasks = new[] { task1, task2 };
try
{
await Task.WhenAll(tasks);
}
catch {}
var results = tasks.Where(t => t.Status == TaskStatus.RanToCompletion)
.Select(t => t.Result)
.ToList();
if (results.Any())
return results;
// or maybe another exception,
// since await handles AggregateException in a weird way
throw new AggregateException(tasks.Select(t => t.Exception));
}
Here is an alternative way of describing Method1Async() and Method2Async(). This is the demonstration of ContinueWith. Answering the question you ask in title – you do can use a different method for each task, that method will be called after the task completes.
var task1 = _httpClient.GetStringAsync("api1's url here").ContinueWith(t => Method1ResultProcessorAsync(t));
var task2 = _httpClient.GetStringAsync("api2's url here").ContinueWith(t => Method2ResultProcessorAsync(t));
You handle the exceptions correctly. Answering "But if all fails, I want to the Get method to throw.": just check whether results.Count == 0 before the return, and throw if it 0.

Categories

Resources