C# 如何使任务超时而不阻塞UI线程?

C# 如何使任务超时而不阻塞UI线程?,c#,task-parallel-library,C#,Task Parallel Library,我有一些调用方法下载文件的代码: private async Task DownloadFile() { WebClient client = new WebClient(); var downloadTask = Task.Run( () => client.DownloadFile("http://www.worldofcats.com/bigkitty.zip",

我有一些调用方法下载文件的代码:

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;
由于它是表单应用程序的一部分,因此在用户界面无响应的情况下下载时不会引起任何问题。唯一的问题是,DownloadFile方法没有超时,有时可能会出错或挂起,所以我需要在其中设置一个超时

如果我使用
Task.Wait(x)然后它会阻止UI线程。我想我可以使用
wait 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;
    }
}
现在,当您
等待下载文件()
时,可以将其包装在
try/catch
块中,以捕获
任务取消异常
(或):

[编辑] 正如在评论中所注意到的,你不能取消一个不知道取消的任务——不知怎么的,我忘记了(boo!)。但不用担心,您可以通过使用和来解决此问题,因此您甚至不需要取消令牌:

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");
}

检查有关睡眠与等待的一些信息。该令牌不能导致正在运行的任务停止。@usr True,不知何故我忘记了这一点(?),但正如编辑中所述,也有一个补救措施:)谢谢,正是它阻止了我使用取消令牌模式。
Wait task.Run(()=>{task.WaitAny(watcher,downloadTask);})
请不要这样写代码。
等待任务。wheny(watcher,downloadTask)
更好。
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");
}