【问题标题】:WebException when calling WebClient.CancelAsync on "await DownloadTaskAsync"在“等待 DownloadTaskAsync”上调用 WebClient.CancelAsync 时出现 WebException
【发布时间】:2019-09-16 03:16:40
【问题描述】:

这很好用:

private WebClient _webClient;

private void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

虽然这段代码(注意 async/await 模式)...:

private WebClient _webClient;

private async void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

...抛出以下异常:

System.Net.WebException

请求被中止:请求被取消。

   at System.Net.ConnectStream.EndRead(IAsyncResult asyncResult)
   at System.Net.WebClient.DownloadBitsReadCallbackState(DownloadBitsState state, IAsyncResult result)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at WpfApp1.MainWindow.<ButtonStart_Click>d__2.MoveNext() in WpfApp1\MainWindow.xaml.cs:line 19

如何取消以await WebClient.DownloadFileTaskAsync() 开始的任务而不引发异常?

【问题讨论】:

  • 你怎么知道下载被中止了?
  • 我怀疑第一个代码块是否真的可以正常工作:您将几乎立即处理_webClient,这意味着它可能在CancelAsync() 被调用时被处理。我猜 CancelAsync 在您忽略的任务中返回一个错误,因为您没有等待它。所以它看起来工作正常,但请求实际上并没有被取消。
  • @PauloMorgado 通过在WebClient.DownloadFileCompleted 事件处理程序中检查AsyncCompletedEventArgs.Cancelled 状态。
  • @StriplingWarrior 好吧,下载停止了,那肯定意味着请求被取消了,不是吗?
  • @Otiel:Touché。我只是想当请求没有取消时你的下载不应该完成。通常,如果你有一个using 语句并且忽略等待异步任务,你会为此而烦恼。但基于this commentthe source codeWebClient 基本上忽略了被处置。

标签: c# async-await webclient


【解决方案1】:

你可以像这样捕获异常:

using (_webClient = new WebClient())
{
    try
    {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
    catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
    {
        Console.WriteLine("Cancelled");
    }
}

更新:如何更改CancelAsync的默认行为,以避免不得不捕获异常:

public static Task<bool> OnCancelReturnTrue(this Task task)
{
    return task.ContinueWith(t =>
    {
        if (t.IsFaulted)
        {
            if (t.Exception.InnerException is WebException webEx
                && webEx.Status == WebExceptionStatus.RequestCanceled) return true;
            throw t.Exception;
        }
        return t.IsCanceled;
    }, TaskContinuationOptions.ExecuteSynchronously);
}

使用示例:

bool cancelled = await _webClient.DownloadFileTaskAsync(
    "https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin").OnCancelReturnTrue();
if (cancelled) Console.WriteLine("Cancelled");

【讨论】:

  • 谢谢。这已经是我正在做的事情,只是想知道是否有更简洁的方法来取消任务而不会引发异常。
  • @Otiel 它按设计抛出异常。显然取消被认为是特殊的。如果你想变得花哨,你可以编造一些自定义扩展方法来根据你的喜好改变这种行为。我已经用一个例子更新了我的答案。
【解决方案2】:

例外是它应该如何工作。

如果您不希望该异常从事件处理程序中传播出去,请捕获该异常。

【讨论】:

    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 1970-01-01
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 2022-11-07
    • 1970-01-01
    • 2018-03-31
    相关资源
    最近更新 更多