Информация об изменениях

Сообщение Re[8]: пару вопросов от 15.03.2020 9:16

Изменено 15.03.2020 9:22 Shmj

Re[8]: пару вопросов
Здравствуйте, RushDevion, Вы писали:

RD>В какой момент?

RD>Если напишешь воспроизводимый тест и ткнешь пальцем в непонятное, я, наверное, смогу объяснить, почему оно так работает.

Вот же:

  Скрытый текст
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices.ComTypes;
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;

                _ = Task.Run(async () =>
                {
                    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); // Добавляем в список на ожидание

                    try
                    {
                        await task;
                    }
                    catch (TaskCanceledException)
                    {
                        // Не спасает...
                    }
                });
            }
        }
    }
}
Re[8]: пару вопросов
Здравствуйте, RushDevion, Вы писали:

RD>В какой момент?

RD>Если напишешь воспроизводимый тест и ткнешь пальцем в непонятное, я, наверное, смогу объяснить, почему оно так работает.

Вот же:

  Скрытый текст
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices.ComTypes;
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;

                _ = Task.Run(async () =>
                {
                    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); // Добавляем в список на ожидание

                    try
                    {
                        await task;
                    }
                    catch (TaskCanceledException)
                    {
                        // Не спасает...
                    }
                });
            }
        }
    }
}


Хотя, в принципе, просто путает вложенность. Срабатывает ожидание для задачи из словаря раньше, чем то ожидание, которое в try/catch. Если переписать так, то ок:

  Скрытый текст
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices.ComTypes;
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;

                _ = Task.Run(async () =>
                {
                    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);

                    try
                    {
                        await task;
                    }
                    catch (TaskCanceledException)
                    {
                    }

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