C# 使用已启动的列表<>取消任务

C# Cancel Task with started List<>

我开始使用列表任务并等待 Task.WhenAll

private async void btn_download_Click(object sender, EventArgs e)
    {
        .
        .
        .
        await DownloadMultipleFilesAsync(old_json);
        Console.WriteLine("Download completed.");
    }

这就是我用列表开始任务的代码。

private async Task DownloadMultipleFilesAsync(List<media> doclist)
    {
        var token = cancelTokenSource.Token;
        await Task.WhenAll(doclist.Select(doc => DownloadFileAsync(doc)));
        btn_download.Enabled = true;
    }

和我的下载方法

private async Task DownloadFileAsync(media media)
{
    .
    .
    .
    Console.WriteLine(media.no + media_ext + " started.");
    webClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
    await webClient.DownloadFileTaskAsync(new Uri(media.url), @downloadToDirectory);
    Console.WriteLine(media.no + media_ext + " finished.");
    .
    .
    .
}

输出window像这样:

1.jpg started.
2.jpg started.
3.jpg started.
4.jpg started.
5.jpg started.
6.jpg started.
7.jpg started.
8.jpg started.
9.jpg started.
10.jpg started.
11.jpg started.
12.jpg started.
13.jpg started.
14.jpg started.
15.jpg started.
16.jpg started.
17.jpg started.
18.jpg started.
19.jpg started.
1.jpg finished.
4.jpg finished.
2.jpg finished.
6.jpg finished.
8.jpg finished.
10.jpg finished.
3.jpg finished.
5.jpg finished.
12.jpg finished.
14.jpg finished.
7.jpg finished.
16.jpg finished.
18.jpg finished.
9.jpg finished.
11.jpg finished.
13.jpg finished.
15.jpg finished.
17.jpg finished.
19.jpg finished.
Download completed.

我想单击 btn_cancel 并取消开始任务并等待完成开始任务。

private void btn_cancel_Click(object sender, EventArgs e)
{
    cancelTokenSource.Cancel();
    cancelTokenSource = new CancellationTokenSource();
}

WebClient 不接受 CancellationToken,这很奇怪。相反,它有一个方法 CancelAsync,您可以调用它来取消挂起的呼叫。

看来您必须保留对 webClient 的引用,并在单击取消按钮时调用该方法。

您需要做的是将取消令牌传递到调用链中,然后在可能的情况下使用它,您还需要注册一个取消回调以调用 WebClient.CancelAsync() 来取消下载。

private async void btn_download_Click(object sender, EventArgs e)
{
    .
    .
    .
    var token = cancelTokenSource.Token;
    try
    {
        await DownloadMultipleFilesAsync(old_json, token);
        Console.WriteLine("Download completed.");
    }
    catch(OperationCanceledException ex)
    {
        //If something other than our token caused the cancel bubble up the exception.
        if(ex.CancellationToken != token)
            throw;
    }
}


private async Task DownloadMultipleFilesAsync(List<media> doclist, CancellationToken token)
{
    await Task.WhenAll(doclist.Select(doc => DownloadFileAsync(doc, token));
    btn_download.Enabled = true;
}


private async Task DownloadFileAsync(media media, CancellationToken token)
{
    .
    .
    .
    Console.WriteLine(media.no + media_ext + " started.");
    webClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
    try
    {
        using(token.Register(() => webClient.CancelAsync()))
        {
            await webClient.DownloadFileTaskAsync(new Uri(media.url), @downloadToDirectory);
        }
    }            
    catch (WebException ex)
    {
        //Raise a OperationCanceledException if the request was canceled, otherwise bubble up the exception.
        if(ex.Status == WebExceptionStatus.RequestCanceled)
            throw new OperationCanceledException(token);
        else
            throw;
    }
    Console.WriteLine(media.no + media_ext + " finished.");
    .
    .
    .
}