Как сделать ограничение на ввод по времени?
Т.е. если в течении какого то времени не был введен правильный ответ (theard или task?), то переменной основного потока передается какое то значение. Этот код не работает, т.к. string test = await PrintAsync(); блокирует основной поток в ожидании значения
class Program
{
async static Task Main(string[] args)
{
string test = await PrintAsync();
async Task <string> PrintAsync()
{
await Task.Delay(3000);
string name = "";
return name;
}
while (true)
{
if(test=="")
{
break;
}
Console.WriteLine("Ввод ответа");
string text = Console.ReadLine();
Console.WriteLine("ответ" + text);
}
}
}
Ответы (1 шт):
Здесь не то что бы борьба с потоками нужна, здесь следует понимать, что консоль не имеет асинхронного API. То есть если вы запросили Console.ReadKey или Console.ReadLine и оно ушло в режим ожидания ввода с консоли, то не сможете это отменить, никак.
Единственный простой лайфхак, как объехать это ограничение, это постоянно оправшивать, есть ли в буфере клавиатуры введенные символы через Console.KeyAvailable.
Так как вы решили делать асинхронно, то есть великолепная вещь - токен отмены CancellationToken, который поможет и с прерыванием ввода, и умеет отменяться сам через какое-то время.
Что ж, теперь зная всё это, осталось написать только свой собственный асинхронный ReadLine, поддерживающий токен отмены.
static async Task<string> ReadLineAsync(CancellationToken token)
{
var sb = new StringBuilder();
ConsoleKey key;
do
{
ConsoleKeyInfo keyInfo = await ReadKeyAsync(token, true);
key = keyInfo.Key;
if (key == ConsoleKey.Backspace && sb.Length > 0)
{
Console.Write("\b \b");
sb.Length--;
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write(keyInfo.KeyChar);
sb.Append(keyInfo.KeyChar);
}
} while (key != ConsoleKey.Enter);
Console.WriteLine();
return sb.ToString();
}
static async Task<ConsoleKeyInfo> ReadKeyAsync(CancellationToken token, bool intercept = false)
{
await WaitForKeyAsync(token);
return Console.ReadKey(intercept);
}
static async Task WaitForKeyAsync(CancellationToken token)
{
while (!Console.KeyAvailable)
{
await Task.Delay(10, token);
}
}
Теперь это можно использовать вот так
static async Task Main(string[] args)
{
const int threshold = 3000;
Console.Write("Сколько будет 2+2? (3 секунды на ответ): ");
using var cts = new CancellationTokenSource();
cts.CancelAfter(threshold);
try
{
string answer = await ReadLineAsync(cts.Token);
if (answer == "4")
Console.WriteLine("Правильно");
else
Console.WriteLine("Неверно");
}
catch (OperationCanceledException)
{
Console.WriteLine();
Console.WriteLine("Время вышло!");
}
}
Кстати, чтобы вручную отменить токен, нужно просто вызвать cts.Cancel().
Проверяю
Правильный ввод
Сколько будет 2+2 (3 секунды на ответ): 4
Правильно
Неправильный
Сколько будет 2+2 (3 секунды на ответ): ffff
Неверно
Без ввода или с вводом без нажатия Enter
Сколько будет 2+2 (3 секунды на ответ):
Время вышло!