Open mikhailshilkov opened 3 years ago
Thank you for this article. I think however your task list will never complete if it has no tasks.
Inspired by your article, I have made my own implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
namespace MyNamespace
{
public class TaskList
{
private HashSet<Guid> _tasks;
private TaskCompletionSource<bool> _firstTask;
private TaskCompletionSource<bool> _tcs;
public TaskList()
{
Reset();
}
public async Task Run()
{
_firstTask.TrySetResult(true);
await _tcs.Task.ConfigureAwait(false);
Reset();
}
private void Reset()
{
_tasks = new HashSet<Guid>();
_firstTask = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Add(_firstTask.Task);
}
public void Add(params Task[] tasks)
=> Add(tasks.AsEnumerable());
public void Add(IEnumerable<Task> tasks)
{
foreach (var task in tasks)
{
Add(task);
}
}
public void Add(Task task)
{
var id = Guid.NewGuid();
lock (_tasks)
{
_tasks.Add(id);
}
HandleCompletion(id, task);
}
private async Task HandleCompletion(Guid id, Task task)
{
try
{
await task.ConfigureAwait(false);
await HandleSuccess(id, task).ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
await HandleCancellation(id, task, ex).ConfigureAwait(false);
}
catch (Exception ex)
{
await HandleError(id, task, ex).ConfigureAwait(false);
}
finally
{
lock (_tasks)
{
_tasks.Remove(id);
if (_tasks.Count == 0)
{
_tcs.TrySetResult(true);
}
}
}
}
protected virtual async Task HandleSuccess(Guid id, Task task)
{
}
protected virtual async Task HandleCancellation(Guid id, Task task, OperationCanceledException ex)
{
}
protected virtual async Task HandleError(Guid id, Task task, Exception ex)
{
}
}
}
Things to note:
TaskList.Run()
completes even i no tasks were added.Guid
. So Task.CompletedTask
is handled correctly.TaskList.Run()
resets the TaskList
. This means that TaskList.Run()
can be run multiple times.HandleSuccess
, HandleCancellation
and/or HandleError
.
Add your comment to How to Drain a List of .NET Tasks to Completion.
The comments will be displayed directly on the page. I may edit or merge your comments in a non-significant way to make sure they are displayed properly on the website.