While working with list of task, we only get two built in options to retrieve task info for completion. First is WhenAny and other WhenAll. WhenAny can be used when any of the given tasks gets completed first where as WhenAll notifies when all of the task gets completed. There is no option to know each task as soon as it gets completed.
I was going through Pro Asynchronous Programming with .NET book which shows better option to deal with notification of task completion efficiently.
To return the task on completion, TaskCompletionSource will be used . Through TaskCompletionSource, Task could be created and with provided inbuilt methods outcome can be handled. This can be also helpful to wrap up legacy codes as well to built up task.
Let's directly look in to core code to return task based on completion.
In the above code at first, list of TaskCompletionSource created based on number of tasks. Afterwards, task continuation is done for each task and result of task is transformed with the help of methods from TaskCompletionSource.
You might worried about TaskContinuationOptions.ExecuteSynchronously .This is on task continuation and the block of code just composing task which is perfectly fine and it won't block the code.
Now above extension function can be attached with any list of task by using OrderByCompletion. Let's see with example:
The code is creating list of task and looping it to get the results from task when any task is getting completed in any sequence.
Source code: https://www.dropbox.com/s/9xemmci3ey40qug/TaskCompletionSequence.zip
I was going through Pro Asynchronous Programming with .NET book which shows better option to deal with notification of task completion efficiently.
To return the task on completion, TaskCompletionSource will be used . Through TaskCompletionSource, Task could be created and with provided inbuilt methods outcome can be handled. This can be also helpful to wrap up legacy codes as well to built up task.
Let's directly look in to core code to return task based on completion.
/// <summary>
/// Order by completion for Task.
/// </summary>
/// <typeparam name="T">Return type</typeparam>
/// <param name="tasks">The tasks.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">tasks</exception>
/// <exception cref="System.ArithmeticException">Must have at least one task</exception>
public static IEnumerable<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> tasks)
{
if (tasks == null)
{
throw new ArgumentNullException("tasks");
}
var allTasks = tasks.ToList();
if (!allTasks.Any())
{
throw new ArithmeticException("Must have at least one task");
}
// Create list of TaskCompletionSource
var taskCompletionSources = new List<TaskCompletionSource<T>>(allTasks.Count);
for (int tskCtr = 0; tskCtr < allTasks.Count; tskCtr++)
{
taskCompletionSources.Add(new TaskCompletionSource<T>());
}
int nextCompletdTask = -1;
for (int nTask = 0; nTask < allTasks.Count; nTask++)
{
taskCompletionSources[nTask] = new TaskCompletionSource<T>();
allTasks[nTask].ContinueWith(tsk =>
{
// Thread safe increment of variable
int taskToComplete = Interlocked.Increment(ref nextCompletdTask);
switch (tsk.Status)
{
case TaskStatus.Canceled:
taskCompletionSources[taskToComplete].SetCanceled();
break;
case TaskStatus.Faulted:
// InnerException is used to avoid AggregateException
taskCompletionSources[taskToComplete].SetException(tsk.Exception.InnerException);
break;
case TaskStatus.RanToCompletion:
taskCompletionSources[taskToComplete].SetResult(tsk.Result);
break;
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
return taskCompletionSources.Select(src => src.Task);
}
In the above code at first, list of TaskCompletionSource created based on number of tasks. Afterwards, task continuation is done for each task and result of task is transformed with the help of methods from TaskCompletionSource.
You might worried about TaskContinuationOptions.ExecuteSynchronously .This is on task continuation and the block of code just composing task which is perfectly fine and it won't block the code.
Now above extension function can be attached with any list of task by using OrderByCompletion. Let's see with example:
private static void Main(string[] args)
{
var uriList = new List<string>()
{
"http://www.google.com",
"http://www.yahoo.com",
"http://www.msn.com",
"http://www.bing.com",
"http://vikutech.blogspot.in",
};
var tasks = GetDownloadedContentTaskAsync(uriList);
foreach (var task in tasks.OrderByCompletion())
{
Console.WriteLine(task.Result);
}
}
/// <summary>
/// Gets the downloaded content asynchronous as task.
/// </summary>
/// <param name="urlString">List of URLs.</param>
/// <returns>Content of downloaded string</returns>
private static IEnumerable<Task<string>> GetDownloadedContentTaskAsync(IEnumerable<string> urlStrings)
{
foreach (var url in urlStrings)
{
var client = new WebClient();
yield return client.DownloadStringTaskAsync(new Uri(url));
// TODO: Check sequence of program sequence
//yield return Task<string>.Factory.StartNew(() =>
//{
// Task.Delay(4000);
// return url;
//});
}
}
The code is creating list of task and looping it to get the results from task when any task is getting completed in any sequence.
Source code: https://www.dropbox.com/s/9xemmci3ey40qug/TaskCompletionSequence.zip
Comments
Post a Comment