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

.NET Core域名检测:PLINQ性能差与信号量结果缺失问题求助

针对.NET Core域名活跃检测项目的性能优化与问题排查方案

先梳理下你遇到的三个核心问题,逐个拆解并给出接地气的解决方案:


问题1:PLINQ实现仅占用单线程,性能极差

你明明设置了WithDegreeOfParallelism(7)和ForceParallelism,却还是单线程跑,核心原因有两个:

  • HttpWebRequest连接数限制:.NET默认ServicePointManager.DefaultConnectionLimit是2,就算开了多线程,所有请求也会排队等可用连接,看起来就像单线程在干活。
  • 同步阻塞拖垮线程池:IsDomainActive里用了同步的GetResponse(),会死死阻塞线程池线程。线程池为了避免过度创建线程,会缓慢扩容,直接导致并行度上不去。

修复建议:

  1. 启动时先调整连接限制:
    ServicePointManager.DefaultConnectionLimit = 200; // 和你计划的并发数匹配
    
  2. 把同步的HttpWebRequest换成异步HttpClient调用(你已经在更新代码里做了,但要注意复用实例),别再阻塞线程池。
  3. 放弃用PLINQ处理异步任务:PLINQ对异步方法支持很差,用Task.WhenAll实现异步并行才是正确姿势。

问题2:Semaphore版本结果数量与输入不匹配

这个问题根儿上就是你没等所有启动的Task完成就结束流程了!看你的代码,foreach里只启动了一堆Task,完全没加等待逻辑,直接进finally释放Semaphore,此时很多任务还没跑完,自然不会把结果塞进allDomains列表。

修复建议:

  1. 收集所有Task,在finally前等它们全部完成:
    var tasks = new List<Task>();
    foreach (var domain in domainList)
    {
        var task = Task.Factory.StartNew(() => {
            // 原有的任务逻辑...
        }, TaskCreationOptions.LongRunning);
        tasks.Add(task);
    }
    // 等所有任务跑完再继续
    Task.WaitAll(tasks.ToArray());
    
  2. 把lock(allDomains)换成线程安全集合ConcurrentBag<BsonDocument>,彻底去掉锁竞争:
    var allDomains = new ConcurrentBag<BsonDocument>();
    // 任务里直接加,不用锁
    allDomains.Add(new BsonDocument { ... });
    
  3. 检查是否有任务静默失败:用Task.WhenAll替代WaitAll,捕获AggregateException,确保异常不会导致任务悄悄挂掉。

问题3:HttpClient+ConcurrentBag的性能问题

你的更新代码里有几个致命的性能坑:

  • 每个请求新建HttpClient:频繁创建HttpClient会导致套接字耗尽(出现大量TIME_WAIT连接),这是.NET里用HttpClient的经典误区。HttpClient是线程安全的,全局复用一个实例就行,或者用HttpClientFactory。
  • 用.Result阻塞异步方法:PLINQ里调用IsDomainActive(domain).Result会把线程池线程卡死,导致线程池饥饿,完全浪费了异步的优势。
  • 超时设置错了:你加的Timeout Header不是标准HTTP头,HttpClient根本不认。正确做法是用CancellationTokenSource设置请求超时。
  • 每个请求新建HttpClientHandler和WebProxy:这些资源创建开销极大,必须复用。

优化后的异步并行方案示例:

// 全局复用的HttpClient实例(或者用HttpClientFactory)
private static readonly HttpClient _httpClient;

static YourClassName()
{
    var netProxy = new WebProxy("<proxyserver>", port)
    {
        Credentials = new NetworkCredential("<login>", "<password>")
    };
    var handler = new HttpClientHandler()
    {
        Proxy = netProxy,
        UseProxy = true,
        MaxConnectionsPerServer = 200 // 单服务器最大连接数,和并发数匹配
    };
    _httpClient = new HttpClient(handler)
    {
        Timeout = TimeSpan.FromSeconds(5) // 全局超时
    };
    _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2");
}

private static async Task<BsonDocument> CheckDomainAsync(string domain)
{
    var url = $"http://{domain}";
    bool isActive = false;
    try
    {
        using var request = new HttpRequestMessage(HttpMethod.Get, url);
        // 只读取响应头,节省带宽
        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
        isActive = response.IsSuccessStatusCode;
    }
    catch
    {
        isActive = false;
    }
    return new BsonDocument
    {
        {"Url", domain},
        {"Active", isActive},
        {"CreatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)},
        {"UpdatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)}
    };
}

// 批量处理域名的方法
private static async Task<List<BsonDocument>> ProcessDomainsAsync(IEnumerable<string> domains, CancellationToken cancellationToken)
{
    // 把每个域名转换成异步任务
    var tasks = domains.Select(domain => CheckDomainAsync(domain));
    // 等所有任务完成,拿结果
    var results = await Task.WhenAll(tasks).ConfigureAwait(false);
    return results.ToList();
}

通用性能优化与排查技巧

  1. 监控资源使用:
    • 用netstat -ano看套接字情况,如果有大量TIME_WAIT,铁定是HttpClient没复用。
    • 用Windows任务管理器或Linuxtop看线程数,如果线程数远低于你设置的并发数,要么是线程池被阻塞,要么是连接数受限。
  2. 调整并发数:8核Windows机器设200-300并发,4核Ubuntu设100-150,别贪多把系统资源榨干。
  3. 异常日志:给每个失败的域名加日志,方便排查为啥结果缺失,避免任务静默失败。
  4. 减少不必要IO:别频繁调用Console.WriteLine,这同步IO开销很大,测试时可以关掉,正式环境用异步日志库(比如Serilog)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:16:57