Это отлично работает:
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
The request was aborted: The request was canceled.
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 Путем проверки статуса AsyncCompletedEventArgs.Cancelled в обработчике событий WebClient.DownloadFileCompleted.
@StriplingWarrior Ну, загрузка остановлена, это наверняка означает, что запрос отменен, не так ли?
@Отиэль: Туше. Я просто подумал, что ваша загрузка не должна завершиться, когда запрос не отменен. Обычно, если у вас есть оператор using и вы пренебрегаете ожиданием асинхронных задач, вы обожжетесь за это. Но, основываясь на этот комментарий и исходный код, WebClient в основном игнорирует удаление.





Исключением является именно то, как работает предполагаемый.
Если вы не хотите, чтобы это исключение распространялось из вашего обработчика событий, перехватите это исключение.
Вы можете поймать исключение следующим образом:
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 выдает исключение по дизайну. Видимо отмена воспринимается как нечто исключительное. Если вы хотите пофантазировать, вы можете придумать какой-нибудь собственный метод расширения, чтобы изменить это поведение по своему вкусу. Я обновил свой ответ примером.
Как узнать, что загрузка была прервана?