You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6 WinForms中Parallel.ForEach参数错误:ParallelLoopState转CancellationToken失败

解决CS1503:无法从ParallelLoopState转换为CancellationToken的错误

错误原因

你在Parallel.ForEach的异步委托里,把第二个参数命名为token,但这个参数的实际类型是**ParallelLoopState**,并非CancellationToken。当你把它传给GetByteArrayAsync的第二个参数时,就会触发类型不匹配的CS1503错误。

另外还有一个隐藏问题:Parallel.ForEach是为CPU密集型同步操作设计的,不适合IO密集型的异步网络请求,直接混用会导致并行任务无法被正确等待(属于"火并忘记"的场景),最终会出现IsBusy提前置为false、任务未完成就返回结果的问题。

修复方案

1. 修正参数命名并传递正确的取消令牌

把Parallel.ForEach委托里的token参数重命名为loopState,调用GetByteArrayAsync时传入外层的cts.Token。

2. 替换Parallel.ForEach为Task.WhenAll实现异步并行

改用Task.WhenAll结合Enumerable.Select来实现真正的异步并行,同时能正确等待所有任务完成。如果需要限制并行度,可以搭配SemaphoreSlim控制同时运行的任务数量。

修改后的完整代码

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace WinFormsApp1
{
    public class ResourceDownloader
    {
        private static Lazy<HttpClient> client = new(() => {
            HttpClientHandler handler = CreateHandler(autoRedirect: true);

            var client = new HttpClient(handler, true) { Timeout = TimeSpan.FromSeconds(60) };
            client.DefaultRequestHeaders.Add("User-Agent", @"Mozilla/5.0 (Windows NT 10; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0");
            client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
            client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
            client.DefaultRequestHeaders.ConnectionClose = true;
            return client;
        }, true);

        private static HttpClientHandler CreateHandler(bool autoRedirect)
        {
            return new HttpClientHandler()
            {
                AllowAutoRedirect = autoRedirect,
                CookieContainer = new CookieContainer(),
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            };
        }

        public record Website(string Url, byte[]? Data, bool Completed = true, Exception? Ex = null);
        public record ProgressReport(Website Site, int PercentageComplete);

        private static object syncObj = new object();
        private static ConcurrentBag<Website> processed = default!;
        private static int progressCount = 0;
        private static int totalCount = 0;

        public static bool IsBusy { get; internal set; } = false;

        public static async Task<List<Website>> Download(IProgress<ProgressReport> progress, IList<string> sites, CancellationTokenSource cts)
        {
            IsBusy = true;
            processed = new ConcurrentBag<Website>();
            progressCount = 0;
            totalCount = sites.Count;

            // 限制并行度为8
            var semaphore = new SemaphoreSlim(8);

            try
            {
                var tasks = sites.Select(async site => {
                    await semaphore.WaitAsync(cts.Token);
                    try
                    {
                        var dataBytes = await client.Value.GetByteArrayAsync(site, cts.Token);
                        ReportProgress(progress, dataBytes, site, null);
                    }
                    catch (Exception ex)
                    {
                        ReportProgress(progress, null, site, ex);
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                });

                await Task.WhenAll(tasks);
            }
            // To Debug / Log
            catch (TaskCanceledException) { Debug.Print("The operation was canceled"); }
            finally { IsBusy = false; }
            return processed.ToList();
        }

        private static void ReportProgress(IProgress<ProgressReport> progress, byte[]? data, string site, Exception? ex)
        {
            lock (syncObj)
            {
                progressCount += 1;
                var percentage = progressCount * 100 / totalCount;
                Website website = new(site, data, ex is null, ex);
                processed.Add(website);
                progress.Report(new ProgressReport(website, percentage));
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 19:06:30