VS2019-Debugger производит высокую загрузку процессора (в зависимости от CancellationTokens?) - PullRequest
2 голосов
/ 06 мая 2020

Мы создаем приложение, которое содержит проблему производителя-потребителя. Более 100 потребителей уведомлены о ценностях. Каждый потребитель работает в Task.Run и проверяет CancellationToken, чтобы отменить его работу.

Если это приложение работает в отладчике VS2019 (последняя версия 16.5.4), оно вызывает высокую загрузку ЦП. После отсоединения отладчика от работающего приложения загрузка ЦП снижается до ожидаемого значения.

Мне удалось устранить проблему с помощью этого фрагмента кода:

private static void Main(string[] args)
{
    Console.WriteLine("Press to start...");
    Console.ReadKey();

    var cts = new CancellationTokenSource();
    var cancellationToken = cts.Token;

    var buffers = (from _ in Enumerable.Range(0, 40)
                   let queueLock = new SemaphoreSlim(1)
                   let queue = new ConcurrentQueue<object>()
                   select (queueLock, queue)).ToList();

    async Task Producer()
    {
        var instance = new object();

        var loop = 0;
        while (!cancellationToken.IsCancellationRequested)
        {
            await Task.Delay(10, cancellationToken);
            foreach (var (queueLock, queue) in buffers)
            {
                queue.Enqueue(instance);
                queueLock.Release();
            }

            ++loop;
            if (loop % 100 == 0)
                Console.WriteLine(loop);
        }
    }

    async Task Consumer(SemaphoreSlim queueLock, ConcurrentQueue<object> queue)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await queueLock.WaitAsync(cancellationToken);
            queue.TryDequeue(out _);
        }
    }

    foreach (var (queueLock, queue) in buffers)
    {
        Task.Run(() => Consumer(queueLock, queue), CancellationToken.None);
    }

    Task.Run(Producer, CancellationToken.None);

    Console.ReadKey();
    cts.Cancel();
}

Следующее изображение показывает поведение. Процесс выделен в диспетчере задач.

enter image description here

Забавно то, что проблема снижается до половины использования процессора, если вы измените строку * С 1017 * на var cancellationToken = CancellationToken.None. Проблема полностью исчезнет, ​​если вы замените SemaphoreSlim и ConcurrentQueue<T> на BlockingCollection<T>. Но это не вариант, потому что это блокирует все потоки в ThreadPool, если значение не создается.

В нашем продуктивном коде мы используем AsyncCollection<T>, что вызывает ту же проблему. В этом случае нам не нужен SemaphoreSlim, но исключим эту коллекцию. Я использовал SemaphoreSlim и ConcurrentQueue<T>, чтобы быть уверенным, что используются только исходные типы.

Что вызывает такое использование ЦП? Как этого избежать?

Надо ли мне об этом беспокоиться?

Мы пытались отключить диагностику VS, но это не решило проблему.

Спасибо за заранее.

Код скомпилирован с. Net Framework 4.7.2 с использованием VS 2019 (16.5.4).

ОБНОВЛЕНИЕ

После намек на Blindy Я сообщил об этой проблеме на github roslyn: https://github.com/dotnet/roslyn/issues/44009

На данный момент это считается ошибкой.

++ ОБНОВЛЕНИЕ

Я пробовал каналы , но проблема осталась.

...