具有超时的WriteAsync

时间:2017-12-13 01:23:59

标签: c# async-await outputstream cancellationtokensource

我尝试使用如下所示的超时编写一个简单的异步写入,并期望该函数在给定非常大的缓冲区和小waitTime时抛出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;
}

从GUI线程调用:

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

1 个答案:

答案 0 :(得分:10)

你无法捕获异步空白。它必须返回一个任务,你必须等待它。

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;
}

gui code

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

取消仍然会发生,但你只是不遵守它们。

更新

os.WriteAsync(可能仅仅是由幕后 1 Task.Run(支持的同步完成。取消令牌不会取消已经运行的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传递给构造函数,那么这是FileOptions.Asynchronous的默认行为