WriteAsync with timeout

2019-07-29 02:02发布

I try to code a simple async write with timeout as below and expect the function to throw a TaskCanceledException given a very large buffer and small waitTime. However, this does not happen. WriteAsync will block for many seconds until the write completes. What am I missing?

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

Call from GUI thread:

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

1条回答
贪生不怕死
2楼-- · 2019-07-29 02:38

You can't catch a async void. It must return a task and you have to await it.

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

The cancellations still happen but you just don't observe them.

Update:

It is possible that os.WriteAsync( is just a synchronous completion just backed by a Task.Run( behind the scenes1. The cancellation token won't cancel a already running Task.Run(. In that case the best way is to wrap the cancellation up in a Task.WhenAny( and pass in the token to there too via a infinitely long 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. For example that is the default behavior of a FileStream if you don't pass inFileOptions.Asynchronous to the constructor

查看更多
登录 后发表回答