WriteAsync с тайм-аутом

Я пытаюсь закодировать простую асинхронную запись с тайм-аутом, как показано ниже, и ожидаю, что функция вызовет TaskCanceledException, учитывая очень большой буфер и малое время ожидания. Однако этого не происходит. WriteAsync будет блокироваться на много секунд до завершения записи. Что мне не хватает?

public async void WriteWithTimeout(Stream os, byte[] buf, int waitMs)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource(waitMs); // cancel after waitMs milliseconds.
    await os.WriteAsync(buf, 0, buf.Length, tokenSource.Token);

    return;
}

Вызов из потока графического интерфейса:

try
{
    WriteWithTimeout(response.OutputStream, buf100M, w1ms);
}
catch(OperationCanceledException e)
{
    ConsoleWriteLine("Failed with exception: {0}", e.Message);
}        

person Chu Bun    schedule 13.12.2017    source источник
comment
не следует ли делать tokenSource.CancelAfter(TimeSpan.FromMilliseconds(waitMs)); ПОСЛЕ WriteAsync, а потом ждать? например этот ответ здесь: stackoverflow.com / questions / 23476576 /   -  person zaitsman    schedule 13.12.2017
comment
Я сумасшедший или это все, что мне нужно сделать: Task task = response.OutputStream.WriteAsync (buf, 0, buf.Length); if (task.Wait (maxWait)) {// ОК. } else {// истекло время ожидания. }   -  person Chu Bun    schedule 14.12.2017


Ответы (1)


Вы не можете поймать асинхронную пустоту. Он должен вернуть задание, и вы должны его дождаться.

public async Task WriteWithTimeout(Stream os, byte[] buf, int waitMs)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource(waitMs); // cancel after waitMs milliseconds.
    await os.WriteAsync(buf, 0, buf.Length, tokenSource.Token);

    return;
}

графический интерфейс

try
{
    await WriteWithTimeout(response.OutputStream, buf100M, w1ms);
}
catch(OperationCanceledException e)
{
    ConsoleWriteLine("Failed with exception: {0}", e.Message);
}

Отмены все еще происходят, но вы их просто не наблюдаете.

Обновлять:

Возможно, что os.WriteAsync( - это просто синхронное завершение, только что подкрепленное Task.Run( за кулисами 1. Токен отмены не отменяет уже запущенный Task.Run(. В этом случае лучший способ - заключить отмену в Task.WhenAny( и передать туда токен через бесконечно длинный Task.Delay(.

public async Task WriteWithTimeout(Stream os, byte[] buf, int waitMs)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource(waitMs); // cancel after waitMs milliseconds.
    var task = os.WriteAsync(buf, 0, buf.Length, tokenSource.Token);
    var waitedTask = await Task.WhenAny(task, Task.Delay(-1, token));
    await waitedTask; //Wait on the returned task to observe any exceptions.
}

1. Например, это поведение по умолчанию для FileStream, если вы не передаете inFileOptions.Asynchronous конструктору

person Scott Chamberlain    schedule 13.12.2017
comment
Ожидание асинхронной задачи работает. Спасибо. Мне нужно предоставить библиотеку некоторым случайным пользователям, и я не хочу беспокоить их async-await. Поэтому я пытаюсь заключить WriteWithTimeout в другую функцию следующим образом: попробуйте {Task task = WriteWithTimeout (response.OutputStream, buf100M, w1ms); } - person Chu Bun; 13.12.2017
comment
(продолжайте с начала. Извините, у меня проблема с форматированием сообщения.) task.Wait (); } catch (Exception e) {} И снова отмена игнорируется, и WriteAsync блокируется до завершения операции записи. - person Chu Bun; 13.12.2017
comment
Вы не можете вызвать task.Wait() в потоке пользовательского интерфейса и заставить пользовательский интерфейс реагировать. Так оно и работает. Если вы не хотите, чтобы ваш пользовательский интерфейс блокировался, вам нужно использовать await или явно запустить код в фоновом потоке, используя Task.Run( в коде графического интерфейса, который его вызывает, а затем использовать this.BeginInvoke(, чтобы вернуться в пользовательский интерфейс, когда работа будет выполнена. изнутри Task.Run( - person Scott Chamberlain; 14.12.2017
comment
Я действительно хочу, чтобы пользовательский интерфейс блокировался, но только на заданные миллисекунды Waitms. Проблема в том, что отмена больше не действует, а пользовательский интерфейс блокируется до завершения операции записи. Другими словами, отмена работает, когда функция пользовательского интерфейса объявляется с помощью asysn и вызывает await в WriteWithTimeout, но отмена игнорируется, когда функция пользовательского интерфейса не объявлена ​​с помощью async и использует task.Wait для вызова WriteWithTimeOut. - person Chu Bun; 14.12.2017
comment
@ChuBun Так работает ли приведенный выше ответ Скотта на вас? Можете ли вы запустить ReadAsync или WaitAsync и увидеть их тайм-аут? Не могли бы вы предоставить обновленную информацию по этому поводу, поскольку я столкнулся с той же проблемой? - person cd491415; 26.02.2019
comment
@ChuBun ... не могли бы вы также предоставить решение для ReadAsync и WriteAsync? Спасибо - person cd491415; 26.02.2019