【问题标题】:WriteAsync with timeoutWriteAsync 超时
【发布时间】:2017-12-13 01:23:58
【问题描述】:

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

【问题讨论】:

  • 您不应该在WriteAsync 之后执行tokenSource.CancelAfter(TimeSpan.FromMilliseconds(waitMs)); 然后等待吗?例如这个答案在这里:stackoverflow.com/questions/23476576/…
  • 我是疯了还是这就是我需要做的一切:Task task = response.OutputStream.WriteAsync(buf, 0, buf.Length); if(task.Wait(maxWait)) { // 好的。 } else { // 超时。 }

标签: c# async-await outputstream cancellationtokensource


【解决方案1】:

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

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代码

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.例如,如果您不将FileOptions.Asynchronous 传递给构造函数,这是FileStream 的默认行为

【讨论】:

  • 等待异步任务工作。谢谢你。我需要为一些临时用户提供一个库,并且不想用 async-await 来打扰他们。所以我尝试将 WriteWithTimeout 包装在另一个函数中,如下所示:try { Task task = WriteWithTimeout(response.OutputStream, buf100M, w1ms); }
  • (从上面继续。抱歉,我在格式化消息时遇到问题。) task.Wait(); } catch(Exception e) { } 再次,取消被忽略,WriteAsync 阻塞,直到写入操作完成。
  • 您不能在 UI 线程上调用 task.Wait() 并让 UI 响应。这就是它的工作方式。如果您不希望您的 UI 锁定,您需要使用 await 或在后台线程中显式运行代码,在调用它的 GUI 代码中使用 Task.Run( 然后使用 this.BeginInvoke( 返回 UI这项工作是在Task.Run( 内部完成的
  • 我实际上希望 UI 被阻止,但仅限于给定的 Waitms 毫秒。问题是取消不再生效,并且 UI 被阻塞,直到写入操作完成。换句话说,当 UI 函数使用 asysn 声明并在 WriteWithTimeout 上调用 await 时,取消有效,但当 UI 函数未使用 async 声明并使用 task.Wait 调用 WriteWithTimeOut 时,取消将被忽略。
  • @ChuBun 那么上述 Scott 的回答对你有用吗?您是否能够启动 ReadAsync 或 WaitAsync 并看到它们超时?当我遇到同样的问题时,你能提供一些更新吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-18
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多