寻找一种更普遍接受的等待WebClient
的模式:
约束:
Task.Run(async () => await method())
Download
方法时,它只需要像返回字符串的普通方法一样行事WebClient.DownloadFileTaskAsync
或DownloadFileAsync
没有区别;只需要能够使用WebClient
目前的实施似乎有效,但似乎并不正确。除了使用while
循环和Thread.Sleep
在使用otherObject.ShouldCancel
时定期检查WebClient
时,是否有更普遍可接受的替代方案?
private string Download(string url)
{
// setup work
string fileName = GenerateFileName();
// download file
using (var wc = new WebClient())
{
wc.DownloadFileCompleted += OnDownloadCompleted
Task task = wc.DownloadFileTaskAsync(url, fileName);
// Need to wait until either the download is completed
// or download is canceled before doing any other work
while (wc.IsBusy || task.Status == TaskStatus.WaitingForActivation)
{
if (otherObject.ShouldCancel)
{
wc.CancelAsync();
break;
}
Thread.Sleep(100);
}
void OnDownloadCompleted(object obj, AsyncCompletedEventArgs args)
{
if(args.Cancelled)
{
// misc work
return;
}
// misc work (different than other work below)
}
}
// Other work after downloading, regardless of cancellation.
// Could include in OnDownloadCompleted as long as this
// method blocked until all work was complete
return fileName;
}
答案 0 :(得分:1)
我希望这有用。 基本上你的包装器使用cancellationToken.Register(webClient.Cancel)注册回调;一旦cancelToken.Cancel()被调用,异步任务应抛出一个异常,您可以按如下方式处理:
public class Client
{
public async Task<string> DownloadFileAsync(string url, string outputFileName, CancellationToken cancellationToken)
{
using (var webClient = new WebClient())
{
cancellationToken.Register(webClient.CancelAsync);
try
{
var task = webClient.DownloadFileTaskAsync(url, outputFileName);
await task; // This line throws an exception when cancellationTokenSource.Cancel() is called.
}
catch (WebException ex) when (ex.Message == "The request was aborted: The request was canceled.")
{
throw new OperationCanceledException();
}
catch (TaskCanceledException)
{
throw new OperationCanceledException();
}
return outputFileName;
}
}
}
尝试此示例的简单方法
private async static void DownloadFile()
{
var cancellationTokenSource = new CancellationTokenSource();
var client = new Client();
var task = client.DownloadFileAsync("url",
"output.exe", cancellationTokenSource.Token);
cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
cancellationTokenSource.Cancel();
try
{
var result = await task;
}
catch (OperationCanceledException)
{
// Operation Canceled
}
}
在更现实的情况下,由于用户交互或回调而引发的事件将调用cancellationTokenSource.Cancel()。
<强>更新强>
另一种方法是订阅DownloadProgressChanged事件,并在调用回调时检查otherObject.ShouldCancel。
以下是一个例子:
public class Client
{
public string Download(string url)
{
// setup work
string fileName = GenerateFileName();
// download file
using (var wc = new WebClient())
{
wc.DownloadProgressChanged += OnDownloadProgressChanged;
wc.DownloadFileCompleted += OnDownloadFileCompleted;
DownloadResult downloadResult = DownloadResult.CompletedSuccessfuly;
void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (otherObject.ShouldCancel)
{
((WebClient)sender).CancelAsync();
}
}
void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
downloadResult = DownloadResult.Cancelled;
return;
}
if (e.Error != null)
{
downloadResult = DownloadResult.ErrorOccurred;
return;
}
}
try
{
Task task = wc.DownloadFileTaskAsync(url, fileName);
task.Wait();
}
catch (AggregateException ex)
{
}
switch (downloadResult)
{
case DownloadResult.CompletedSuccessfuly:
break;
case DownloadResult.Cancelled:
break;
case DownloadResult.ErrorOccurred:
break;
}
}
// Other work after downloading, regardless of cancellation.
// Could include in OnDownloadCompleted as long as this
// method blocked until all work was complete
return fileName;
}
}
public enum DownloadResult
{
CompletedSuccessfuly,
Cancelled,
ErrorOccurred
}