如何在不阻塞 UI 线程的情况下使任务超时
本文关键字:情况下 任务 超时 线程 UI | 更新日期: 2023-09-27 18:30:43
我有一些代码调用下载文件的方法:
private async Task DownloadFile()
{
WebClient client = new WebClient();
var downloadTask =
Task.Run(
() =>
client.DownloadFile("http://www.worldofcats.com/bigkitty.zip",
"c:''cats''"
);
await downloadTask;
}
为了调用此方法,我这样做:
var downloadTask = DownloadFile();
await downloadTask;
由于它是表单应用程序的一部分,因此在 UI 无响应的情况下下载时不会产生问题。唯一的问题是,DownloadFile 方法没有超时,有时它可能会出错或挂起,所以我需要输入超时。
如果我使用 Task.Wait(x);
那么它会阻止 UI 线程。我想我可以使用await Task.WhenAny(downloadTask, () => Thread.Sleep(50000));
但我不确定这是否是最好的方法。
所以我的问题是,我应该怎么做才能解决这个问题,如果它被强制终止,我该如何清理我的任务?(或者我必须担心吗?
较旧的解决方案,不适用于无法识别取消的任务
你应该通过一个CancellationToken
:
private async Task DownloadFile()
{
WebClient client = new WebClient();
using(var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60))
{
var downloadTask =
Task.Run(
() =>
client.DownloadFile("http://www.worldofcats.com/bigkitty.zip",
"c:''cats''"),
cts.Token
);
await downloadTask;
}
}
现在,当您await DownloadFile()
时,您可以将其包装在try/catch
块中以捕获TaskCanceledException
(或OperationCanceledException
):
try
{
await DownloadFile();
}
catch(TaskCanceledException)
{
//Timeout!
}
[编辑]
正如评论中指出的那样,您无法取消无法识别取消的任务 - 不知何故我忘记了这一点(嘘!但不用担心,你可以通过使用 DownloadFileTaskAsync
和 CancelAsync
来解决这个问题,所以你甚至不需要取消令牌:
var downloadTask = client.DownloadFileTaskAsync("http://www.worldofcats.com/bigkitty.zip",
"c:''cats''");
var timerTask = Task.Delay(TimeSpan.FromSeconds(60));
await Task.WhenAny(downloadTask, timerTask);
client.CancelAsync(); // This does nothing if there's no operation in progress, as noted in documentation
检查出来了:
private static async void Test()
{
var source = new CancellationTokenSource();
var watcher = Task.Delay(TimeSpan.FromSeconds(4), source.Token);
var downloadTask = Task.Factory.StartNew(() =>
{
//.. Simulating a long time task
Thread.Sleep(TimeSpan.FromSeconds(10));
},
source.Token);
await Task.Run(() => { Task.WaitAny(watcher, downloadTask); });
source.Cancel();
if (!downloadTask.IsCompleted)
Console.WriteLine("Time out!");
else
Console.WriteLine("Done");
}