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

C# .NET Selenium网页爬虫是否可通过多线程与并行编程提升效率?

问题根因分析

你的并行完全没有生效,核心是2个致命错误导致的:

  • 你将核心耗时逻辑LoadBrowser完整包裹在全局lock块中,同一时间仅允许1个线程执行这段代码,所有线程都要排队执行,10条数据自然就是10*10=100秒,和单线程没有任何区别。
  • 并行逻辑重复嵌套:Parallel.ForEach本身已经会分配线程执行任务,你在内部又手动创建了新的Thread,属于完全多余的操作,还额外增加了线程调度、上下文切换的开销,完全没有必要。
多线程与并行编程的实际运行逻辑
  • Parallel.ForEach默认适配CPU密集型任务,会根据当前设备的CPU核心数自动分配并行度,你的场景是IO密集型任务(90%以上的耗时在等待网页加载、网络请求响应,CPU处于空闲状态),完全可以设置远高于CPU核心数的并行度,只要不触发目标网站的反爬限制即可。
  • 并行的核心是让等待IO的时间可以被其他任务利用,而不是真的同时跑10个CPU运算任务,所以IO密集型任务的并行上限通常远高于CPU核心数。
  • 你的场景下Parallel.ForEach本身可以满足需求,但错误的锁使用和冗余的线程创建抵消了所有并行收益。
代码优化方案

核心优化点

  1. 移除冗余的手动Thread创建、线程状态轮询、Thread.Abort(该方法已被.NET标记为过时,会引发不可预期的进程异常)
  2. 缩小锁的范围,仅在操作公共非线程安全集合时加锁,或者直接使用线程安全集合ConcurrentBag<GoogleList>规避锁的使用
  3. 显式设置Parallel.ForEach的并行度,匹配你的业务需求

优化后示例代码

private static List<GoogleList> MultiTreadMain(List<FileStructure> values)
{
    // 线程安全集合,无需手动加锁即可多线程写入
    ConcurrentBag<GoogleList> listGInfo = new ConcurrentBag<GoogleList>();
    // 提前筛选需要处理的数据,避免在Parallel内部做无效判断
    var processValues = values.Where(v => v.ID <= 10).ToList();
    
    Parallel.ForEach(processValues, new ParallelOptions
    {
        // 按需设置并行度,测试时可以设为10,正式跑可以根据反爬情况调整为20-50
        MaxDegreeOfParallelism = 10
    }, value =>
    {
        // 耗时逻辑不需要加锁,每个线程独立执行
        var singleListGInfo = LoadBrowser("https://www.google.com", value.Address, value.City, value.State,
            value.FirstName, value.LastName, "USA", value.ZipCode, value.ID);
        // 写入线程安全集合
        foreach (var item in singleListGInfo)
        {
            listGInfo.Add(item);
        }
    });

    return listGInfo.ToList();
}

额外优化建议

  • 每个爬虫线程使用独立的Selenium浏览器实例,避免多线程共用同一个浏览器导致的操作冲突
  • 可以开启Selenium无头模式,减少浏览器UI渲染的额外开销
  • 若需要更高的性能,可以考虑把Selenium替换为轻量级的HTTP请求库(如HttpClient)结合HTML解析库(如AngleSharp),性能可以提升数倍。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 00:48:01