Concurrency with C Sharp
Created on: 08 Jun 24 12:27 +0700 by Son Nguyen Hoang in English
Good practices to use C# with Task and more
After a while coding in C#, I think myself need to train hard on Concurrency. After, this is such a powerful concept but I never tried to grasp the ideas properly.
I started teaching myself about Concurrency in C# using the book Concurrency in C#: Cookbook
. This short article will summarize some practices and snipplets that I found useful.
Some quick notes:
- Avoid
async void
(should beasync Task
) - Use
Task<T>
rather thanValueTask<T>
(if possible) - Becareful with
ConfigureAwait()
and its implementation on UI Threads.
1. Visualize for IProgress<T> and Task
public static async Task MethodProgress(IProgress<double> progress = null){
bool done = false;
double percentageComplete = 0;
while (!done){
progress?.Report(percentageComplete);
percentageComplete += 0.15;
if (percentageComplete >= 1)
break;
await Task.Delay(TimeSpan.FromMilliseconds(40));
}
}
public static async Task TestProgress()
{
var progress = new Progress<double>();
progress.ProgressChanged += (sender, args) =>
{
Console.WriteLine($"Progress: {args}%");
};
await MethodProgress(progress);
}
2. Using Task.WhenAll()
- The book author explicitly advice to use array (non
IEnumerable
instead) so he convert all to array first.
async Task<string> DownloadAllAsync(HttpClient client,
IEnumerable<string> urls)
{
// Define the action to do for each URL.
var downloads = urls.Select(url => client.GetStringAsync(url));
// Note that no tasks have actually started yet
// because the sequence is not evaluated.
// Start all URLs downloading simultaneously.
Task<string>[] downloadTasks = downloads.ToArray();
// Now the tasks have all started.
// Asynchronously wait for all downloads to complete.
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
3. Progressing tasks in order of completion
public static async Task AwaitAndProgressAysn(Task<int> task){
int result = await task;
Console.WriteLine(result);
}
public static async Task ProgressTasksAsyn2()
{
Task<int> taskA = DelayAndReturnAsyn(1);
var taskB = DelayAndReturnAsyn(2);
var taskC = DelayAndReturnAsyn(5);
var taskD = DelayAndReturnAsyn(3);
var tasks = new[] { taskA, taskB, taskC, taskD };
IEnumerable<Task> taskQuery = from t in tasks select AwaitAndProgressAysn(t);
Task[] processingTasks = taskQuery.ToArray();
await Task.WhenAll(processingTasks);
}
// Using Nito.AsynEx to have .OrderByCompletion()
public static async Task ProgressTasksAsyn3()
{
Task<int> taskA = DelayAndReturnAsyn(1);
var taskB = DelayAndReturnAsyn(2);
var taskC = DelayAndReturnAsyn(5);
var taskD = DelayAndReturnAsyn(3);
var tasks = new[] { taskA, taskB, taskC, taskD };
foreach (var task in tasks.OrderByCompletion())
{
var result = await task;
Console.WriteLine(result);
}
}
}