How do get all tasks in - c#

How i can get an iterator for use in Task.WaitAll to wait for all task exist in customList with lowest overhead or lines of code ?
public class Custom
{
public Task task;
public int result;
}
public class Main
{
void doSomething()
{
List<Custom> customList=new List<>();
//customList.Add(custom1,2,3,4,5.....);
Task.WaitAll(????)
}
}

Tasks aren't threads. They represent the execution of a function, so there is no point in storing them and their results as classes. Getting the results of multiple tasks is very easy if you use Task.WhenAll.
There is no reason to wrap the task in a class. You can think of Task itself as the wrapper over a thread invocation and is result.
If you want to retrieve the contents from multiple pages, you can use LINQ a single HttpClient to request the content from many pages at once. await Task.WhenAll will return the results from all tasks :
string[] myUrls=...;
HttpClient client=new HttpClient();
var tasks=myUrls.Select(url=>client.GetStringAsync(url));
string[] pages=await Task.WhenAll(tasks);
If you want to return more data, eg check the contents, you can do so inside the Select lambda:
var tasks=myUrls.Select(async url=>
{
var response=await client.GetStringAsync(url);
return new {
Url=url,
IsSensitive=response.Contains("sensitive"),
Response=response
};
});
var results =await Task.WhenAll(tasks);
Results contains the anonymous objects generated inside Select. You can iterate or query them just like any other array, eg:
foreach(var result in results)
{
Console.WriteLine(result.Url);
}

Related

How does parallelization work on async/await?

I have the following code, that I intend to run asynchronously. My goal is that GetPictureForEmployeeAsync() is called in parallel as many times as needed. I'd like to make sure that 'await' on CreatePicture does not prevent me from doing so.
public Task<Picture[]> GetPictures(IDictionary<string, string> tags)
{
var query = documentRepository.GetRepositoryQuery();
var employees = query.Where(doc => doc.Gender == tags["gender"]);
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
}
private Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
return CreatePicture(tags, base64PictureTask, documentTask);
}
private static async Task<Picture> CreatePicture(IDictionary<string, string> tags, Task<string> base64PictureTask, Task<Employee> documentTask)
{
var document = await documentTask;
return new Picture
{
EmployeeID = document.ID,
Data = await base64PictureTask,
ID = document.ID.ToString(),
Tags = tags,
};
}
If I understand it correctly, Task.WhenAll() is not affected by the two awaited tasks inside CreatePicture() because GetPictureForEmployeeAsync() is not awaited. Am I right about this? If not, how should I restructure the code to achieve what I want?
I'd like to make sure that 'await' on CreatePicture does not prevent me from doing so.
It doesn't.
If I understand it correctly, Task.WhenAll() is not affected by the two awaited tasks inside CreatePicture() because GetPictureForEmployeeAsync() is not awaited. Am I right about this?
Yes and no. The WhenAll isn't limited in any way by the awaited tasks in CreatePicture, but that has nothing to do with whether GetPictureForEmployeeAsync is awaited or not. These two lines of code are equivalent in terms of behavior:
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
return Task.WhenAll(employees.Select(async employee => await GetPictureForEmployeeAsync(employee, tags)));
I recommend reading my async intro to get a good understanding of how async and await work with tasks.
Also, since GetPictures has non-trivial logic (GetRepositoryQuery and evaluating tags["gender"]), I recommend using async and await for GetPictures, as such:
public async Task<Picture[]> GetPictures(IDictionary<string, string> tags)
{
var query = documentRepository.GetRepositoryQuery();
var employees = query.Where(doc => doc.Gender == tags["gender"]);
var tasks = employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)).ToList();
return await Task.WhenAll(tasks);
}
As a final note, you may find your code cleaner if you don't pass around "tasks meant to be awaited" - instead, await them first and pass their result values:
async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
await Task.WhenAll(base64PictureTask, documentTask);
return CreatePicture(tags, await base64PictureTask, await documentTask);
}
static Picture CreatePicture(IDictionary<string, string> tags, string base64Picture, Employee document)
{
return new Picture
{
EmployeeID = document.ID,
Data = base64Picture,
ID = document.ID.ToString(),
Tags = tags,
};
}
The thing to keep in mind about calling an async method is that, as soon as an await statement is reached inside that method, control immediately goes back to the code that invoked the async method -- no matter where the await statement happens to be in the method. With a 'normal' method, control doesn't go back to the code that invokes that method until the end of that method is reached.
So in your case, you can do the following:
private async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
// As soon as we get here, control immediately goes back to the GetPictures
// method -- no need to store the task in a variable and await it within
// CreatePicture as you were doing
var picture = await blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var document = await documentRepository.GetItemAsync(employee.ID.ToString());
return CreatePicture(tags, picture, document);
}
Because the first line of code in GetPictureForEmployeeAsync has an await, control will immediately go right back to this line...
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
...as soon as it is invoked. This will have the effect of all of the employee items getting processed in parallel (well, sort of -- the number of threads that will be allotted to your application will be limited).
As an additional word of advice, if this application is hitting a database or web service to get the pictures or documents, this code will likely cause you issues with running out of available connections. If this is the case, consider using System.Threading.Tasks.Parallel and setting the maximum degree of parallelism, or use SemaphoreSlim to control the number of connections used simultaneously.

Async/Await in loop behaviour

Say I had a list of algorithms, each containing an async call somewhere in the body of that algorithm. The order in which I execute the algorithms is the order in which I want to receive the results. That is, I want the AlgorithmResult List to look like {Algorithm1Result, Algorithm2Result, Algorithm3Result} after all the algorithms have executed. Would I be right in saying that if Algorithm1 and 3 finished before 2 that my results would actually be in the order {Algorithm1Result, Algorithm3Result, Algorithm2Result}
var algorithms = new List<Algorithm>(){Algorithm1, Algorithm2, Algorithm3};
var algorithmResults = new List<AlgorithmResults>();
foreach (var algorithm in algorithms)
{
algorithmResults.Add(await algorithm.Execute());
}
NO, Result would be in the same order you added it to the list, since each operation is being waited for separately.
class Program
{
public static async Task<int> GetResult(int timing)
{
return await Task<int>.Run(() =>
{
Thread.Sleep(timing * 1000);
return timing;
});
}
public static async Task<List<int>> GetAll()
{
List<int> tasks = new List<int>();
tasks.Add(await GetResult(3));
tasks.Add(await GetResult(2));
tasks.Add(await GetResult(1));
return tasks;
}
static void Main(string[] args)
{
var res = GetAll().Result;
}
}
res anyway contains list in order it was added, also this is not parallel execution.
Since you don't add the tasks, but the results of the task after awaiting, they will be in the order you want.
Even if you did not await the result before adding the next task you could get the order you want:
in small steps, showing type awareness:
List<Task<AlgorithmResult>> tasks = new List<Task<AlgorithmResult>>();
foreach (Algorithm algorithm in algorithms)
{
Task<AlgorithmResult> task = algorithm.Execute();
// don't wait until task Completes, just remember the Task
// and continue executing the next Algorithm
tasks.Add(task);
}
Now some Tasks may be running, some may already have completed. Let's wait until they are all complete, and fetch the results:
Task.WhenAll(tasks.ToArray());
List<AlgrithmResults> results = tasks.Select(task => task.Result).ToList();

Is it possible to return a list of async items in an async method from an async source?

Essentially I'm trying to be able to do this:
var thingTasks = thingFactory.GetMyThings();
// ...
var things = await thingTasks;
I'm trying to start from a list of objects, iterate through that list making an async call for each one, and returning the set of results in an await-able way so the consumer can choose when to await it. GetMyThings itself uses await before generating the list, so it needs to be async itself and is something like:
public async Task<List<Thing>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(async thing => await thing.GetAsync());
return things;
}
The basic idea is that I have some await lines, then after that I use the results of those lines to generate a list and generating each item also requires an async call. I'm trying to avoid blocking within the method (e.g. .Result) and instead pass that responsibility/opportunity back to the caller. Basically, start the tasks in the list but not await them. This naturally makes me want to return Task<List<Thing>> or 'List>`.
The closest I got was return Task.WhenAll(things) but that didn't work (it needed to be Task<Task<Thing[]>> and await await GetMyThings(). Alternatively, return Select(...) returning a Task<List<Task<Thing>>> and needing a await Task.WhenAll(await GetMyThings()) on the consuming side.
In both cases one needs double await statements to realize the list. I'm thinking it's impossible but is there a way to avoid the double await?
Use Task.WhenAll to await all task at once. This way you will run each GetAsync approximately at the same time. So :
Start all task
Await all
Return task's results
Like this :
public async Task<List<Thing>> GetMyThings()
{
var thingMakers = await GetThingMakers();
var tasks = thingMakers.Select(thing => thing.GetAsync());
var things = await Task.WhenAll(tasks);
return things.ToList();
}
If you want to make the inner tasks await-able outside, you need to actually return them:
public async Task<List<Task<Thing>>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(thing => thing.GetAsync());
return things.ToList();
}
You can then use this call like this:
List<Task<Thing>> thingTasks = await GetMyThings();
await Task.WhenAll(thingTasks);
List<Thing> = thingTasks.Select(t => t.Result).ToList();
Or even:
List<Thing> things = await GetMyThings()
.ContinueWith(async r =>
{
await Task.WhenAll(r);
return r.Select(r => r.Result).ToList();
});

Trouble with AWAIT / ASYNC and WebApi with FOREACH

I have multiple async methods that return the same type from different OAuth based REST Api calls.
If I call one directly, I can get the data back:
//Call a specific provider
public async Task<List<Contacts>> Get()
{
return await Providers.SpecificProvider.GetContacts();
}
However, if I try to loop through multiple accounts, the object is returning before the AWAIT finishes:
//Call all providers
public async Task<List<Contacts>> Get()
{
return await Providers.GetContactsFromAllProviders();
}
public async Task<List<Contacts>> GetContactsFromAllProviders()
{
var returnList = new List<Contacts>();
//Providers inherits from List<>, so it can be enumerated to trigger
//all objects in the collection
foreach (var provider in Providers)
{
var con = await provider.GetContacts();
returnList.Add(con);
}
return returnList;
}
I'm new to async and am probably missing something simple
The code you have provided will call the web service for each provider one by one in a serial fashion. I do not see how your statement the object is returning before the AWAIT finishes can be true because inside the loop the call to GetContacts is awaited.
However, instead of calling the providers serially you can call them in parallel which in most cases should decrease the execution time:
var tasks = providers.Select(provider => provider.GetContacts());
// Tasks execute in parallel - wait for them all to complete.
var result = await Task.WhenAll(tasks);
// Combine the returned lists into a single list.
var returnList = result.SelectMany(contacts => contacts).ToList();

Organizing task results using print statements

I am sending urls the are in a list using the task, BeginGetResponse, EndGetResponse and fromasync, and continuewith methods. Using Console.WriteLine is there a way to organize/schedule each urls results when they come back? When I try to handle this the print statements are out of sync.
Example:
1.url:google.com
-ResponseStatus: up
-Sent time
-Received Time
2. url yahoo.com
etc
If you want to print the results in a particular order, rather than in the order that they happen to complete, then don't add the print statements in ContinueWith calls to each task. Instead call WhenAll on a collection of all of the tasks and then add a continuation to that which prints all of the values.
public static void AddPrintStatements(IEnumerable<Task<string>> tasks)
{
Task.WhenAll(tasks)
.ContinueWith(t =>
{
foreach (var line in t.Result)
PrintResults(line);
});
}
Since you're using 4.0 and don't have a WhenAll, you can use this instead:
public static Task<IEnumerable<T>> WhenAll<T>(IEnumerable<Task<T>> tasks)
{
return Task.Factory.ContinueWhenAll(tasks.ToArray(),
results => results.Select(t => t.Result));
}
If you want the results to be printed as they come in, but also maintaining their order, then you could do that too by going through each task and adding a continuation that is both of the previous continuation and the given task:
public static void AddPrintStatements2(IEnumerable<Task<string>> tasks)
{
Task continuation = Task.FromResult(true);
foreach (var task in tasks)
{
continuation = continuation.ContinueWith(t =>
task.ContinueWith(t2 => PrintResults(t2.Result)))
.Unwrap();
}
}
Since you're using 4.0 you also won't have FromResult, so you can use this:
public static Task<T> FromResult<T>(T result)
{
var tcs = new TaskCompletionSource<T>();
tcs.SetResult(result);
return tcs.Task;
}

Categories