2

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

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));,但我不确定这是否是最好的方法。

所以我的问题是,我应该怎么做才能解决这个问题,如果它被强行终止,我该如何清理我的任务?(或者我需要担心这个吗?)

4

2 回答 2

3

较旧的解决方案,不适用于无法识别取消的任务

你应该通过一个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!
}

[编辑]

正如评论中所注意到的,您无法取消不知道取消的任务 - 不知何故我忘记了这一点(嘘!)。但不用担心,您可以使用DownloadFileTaskAsyncand来解决这个问题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
于 2013-09-09T11:06:04.430 回答
-1

检查结束:

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");
}
于 2013-09-09T11:20:13.363 回答