Дождаться пока все завершат работу...
От: Shmj Ниоткуда  
Дата: 12.03.20 12:27
Оценка:
По событию создается долгоживущая задача:

Task.Run(()=> ...


Таких задач может одновременно быть много. Нужно: завершить все задачи и дождаться пока они все заврешат работу (внутри задачи могут быть не асинхронные вызовы, которые отменить с помощью CancellationToken нельзя).

Вот тестовый код:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp45
{
    class Program
    {
        private static readonly CancellationTokenSource TokenSource = new CancellationTokenSource();

        static async Task Main(string[] args)
        {
            var cancellationToken = TokenSource.Token;

            AddTasksPeriodically(cancellationToken);

            Thread.Sleep(1000);

            await StopAllTasksAndWait();
            Console.WriteLine("StopAllTasksAndWait");

            Console.ReadKey();
            Console.WriteLine("Hello World!");
        }

        static async Task StopAllTasksAndWait()
        {
            // Вот тут нужно отменить все и дождаться (вернуть Task) когда все задачи будут завершены
        }

        static void AddTasksPeriodically(CancellationToken cancellationToken)
        {
            // Одновременно стартуем 10 задач
            for (int taskId = 1; taskId <= 10; taskId++)
            {
                int taskIdCopy = taskId;

                if (cancellationToken.IsCancellationRequested)
                    break;

                var task = Task.Run(async () =>
                {
                    Console.WriteLine("Begin " + taskIdCopy);
                    Thread.Sleep(2000); // Вызываем синхронный метод без возможности отмены
                    Console.WriteLine("End " + taskIdCopy);

                    await Task.Delay(4000, cancellationToken); // Задержка
                }, cancellationToken);
            }
        }
    }
}


Решение:

  Скрытый текст
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp45
{
    class Program
    {
        private static readonly CancellationTokenSource TokenSource = new CancellationTokenSource();
        private static readonly ConcurrentDictionary<int, Task> Tasks = new ConcurrentDictionary<int, Task>();

        static async Task Main(string[] args)
        {
            var cancellationToken = TokenSource.Token;

            AddTasksPeriodically(cancellationToken);

            Thread.Sleep(1000);

            await StopAllTasksAndWait();
            Console.WriteLine("StopAllTasksAndWait");

            Console.ReadKey();
            Console.WriteLine("Hello World!");
        }

        static async Task StopAllTasksAndWait()
        {
            Console.WriteLine("Cancel");
            TokenSource.Cancel();

            try
            {
                await Task.WhenAll(Tasks.Values);
            }
            catch (TaskCanceledException taskCanceledException)
            {
                Console.WriteLine("TaskCanceledException");
            }
            catch (AggregateException aggregateException)
            {
                Console.WriteLine("AggregateException (cnt=" + aggregateException.InnerExceptions.Count + ")");
            }
        }

        static void AddTasksPeriodically(CancellationToken cancellationToken)
        {
            // Одновременно стартуем 10 задач
            for (int taskId = 1; taskId <= 10; taskId++)
            {
                int taskIdCopy = taskId;

                if (cancellationToken.IsCancellationRequested)
                    break;

                var task = Task.Run(async () =>
                {
                    Console.WriteLine("Begin " + taskIdCopy);
                    Thread.Sleep(2000); // Вызываем синхронный метод без возможности отмены
                    Console.WriteLine("End " + taskIdCopy);

                    await Task.Delay(4000, cancellationToken); // Задержка

                    Tasks.TryRemove(taskIdCopy, out _); // Удаляем из списка (уже исполнена, нет смысла ждать завершения)
                }, cancellationToken);

                Tasks.TryAdd(taskIdCopy, task); // Добавляем в список на ожидание
            }
        }
    }
}


Смущает что контроль хода программы организован через Exception. Как лучше?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.