Не могу добиться результата чтобы при получении ввода (АСИНХРОННО) цикл сделал паузу вывел нажатую кнопку с корректным отображением цвета в консоли.
Console.ReadKey() - синхронная операция. Она никак не может выполняться асинхронно. Ваша задача в текущей формулировке невыполнима.
Сейчас цикл отрабатывает только после того как я нажму кнопку ну и явная проблема с зацикленным созданием потоков.
Цикл, внутри синхронный метод, который держит код на паузе, пока вы нажмете кнопку - ничего необычного. От того что вы засовываете его в другой поток или 100000 потоков, его поведение не меняется.
Мне нужно чтоб в отдельном потоке считывалась нажатая кнопка
Пожалуйста
ConsoleKeyInfo keyInfo = await Task.Run(() => Console.ReadKey(true));
Только зачем? Ваша задача легко решается синхронно.
static void Main(string[] args)
{
for (int i = 0; i < 1000; i++)
{
if (Console.KeyAvailable)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(Console.ReadKey(true));
Console.ResetColor();
}
Console.WriteLine(i);
Thread.Sleep(10);
}
}
Готово.
Еще раз, ваша задача не решается асинхронностью, она решается, если уж приспичило, многопоточностью, не путайте.
Чтобы не умирать над кодом, разбивайте его на методы. То что вы синтаксически умеете использовать делегаты - хорошо. Но не следует жертвовать читабельностью кода ради этого.
Разделить выполнение на 2 потока легко, а вот чтобы они не поругались, придется синхронизировать доступ к консоли. Например с помощью lock.
Поругаться могут так:
- 1 поток выставляет красный цвет
- 2 поток выводит число
- 1 поток выводит клавишу
- 1 поток выставляет стандартный цвет
В результате получится красное число в консоли рядом с красной клавишей. Вероятность не велика, но даже если такая вероятность есть, код считается непотокобезопасным.
static readonly object _lock = new object();
static async Task Main(string[] args)
{
using var cts = new CancellationTokenSource();
Task task = Task.Run(() => ReadKeyLoop(cts.Token));
for (int i = 0; i < 1000; i++)
{
PrintNumber(i);
await Task.Delay(10);
}
cts.Cancel();
await Task;
Console.WriteLine("Готово. Нажми любую клавишу...");
Console.ReadKey(true);
}
static void ReadKeyLoop(CancellationToken token)
{
Console.WriteLine("Вход в цикл чтения клавиатуры.")
while (!token.IsCancellationRequested)
{
if (Console.KeyAvailable)
PrintRedKey(Console.ReadKey(true).Key);
Thread.Sleep(100);
}
Console.WriteLine("Выход из цикла чтения клавиатуры.")
}
static void PrintRedKey(ConsoleKey key)
{
lock (_lock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(key);
Console.ResetColor();
}
}
static void PrintNumber(int number)
{
lock (_lock)
{
Console.WriteLine(number);
}
}
CancellationToken - это потокобезопасный примитив, позволяющий передать в другой поток событие отмены. Хорошая замена небезопасной глобальной bool переменной, плюс он много всего другого может, осваивайте этот полезный класс.
Метод ReadKeyLoop синхронный, потому что там нет повода для асинхронности. Не городите async/await и потоки без надобности. Это сделает ваш код неэффективным.
Метод Main асинхронный только потому что я счел забавным использовать асинхронное ожидание завершения цикла чтения клавиатуры. Всегда пишите код так, чтобы перед выходом из приложения все запущенные потоки завершались нормально, самостоятельно, а не убивались операционной системой при закрытии процесса. Например вы записываете файл и тут юзер закрыл окно приложения, файл будет брошен недописанным. Поэтому надо грамтно начать завершение всех потоков, закрыть открытые файлы, сетевые подключения и только после этого позволить окну закрыться.
Вот еще несколько полезных ссылок:
100000ваш пул потоков мертв – aepot Sep 08 '21 at 18:47