寻找更普遍可接受的等待 WebClient
的模式:
- 下载文件(可能需要几百毫秒或几分钟(
- 等待下载完成之前完成任何其他工作
- 定期检查另一堂课的标志(布尔(并取消下载,如果需要(无法修改此类(
约束:
- 除非是
Task.Run(async () => await method())
的线条,否则不能使用异步/等待 - 当调用
Download
方法时,它只需要像返回字符串的普通方法一样行为 - 可以使用.NET 4.5和Roslyn编译器的任何功能
- 是否使用
WebClient.DownloadFileTaskAsync
或DownloadFileAsync
没有区别;只需要使用WebClient
取消下载的能力
当前的实现似乎正在起作用,但似乎并不正确。与使用while
循环和Thread.Sleep
相比,使用CC_1时会定期检查otherObject.ShouldCancel
?
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;
}
我希望这很有帮助。基本上,您的包装器使用concellationToken.Register(webclient.cancel(注册回调;一旦调用comcellationToken.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.Status == WebExceptionStatus.RequestCanceled)
{
throw new OperationCanceledException();
}
catch (AggregateException ex) when (ex.InnerException is WebException exWeb && exWeb.Status == WebExceptionStatus.RequestCanceled)
{
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
}
}
在更现实的场景中,concellationTokenSource.cancel((将由用户互动或回调引起的事件调用。
update
另一种方法是订阅下载progresschanged事件,并检查其他访问时。
这是一个示例:
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
}