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

使用WebClient DownloadStringAsync时抛出TargetInvocationException的解决办法

修复WebClient异步下载网页的TargetInvocationException问题及优化方案

异常原因分析

TargetInvocationException是.NET包装异步操作内部异常的容器,你的代码里空catch {}块吃掉了所有异常,导致看不到真正的错误根源——可能是无效URL、网络连接失败、服务器返回HTTP错误(如404/500)、编码解析错误等。

同时你的代码存在几个核心逻辑问题,不仅会导致异常,还会让下载结果完全不符合预期:

  1. 方法在所有异步下载任务完成前就返回了HashSet,调用者拿到的会是空集合
  2. 循环中捕获i的引用,可能导致多个回调使用同一个i值(闭包捕获的是变量本身,不是迭代的当前值)
  3. 每个请求新建WebClient,资源利用率低,且没有妥善释放资源
  4. HashSet.ElementAt(i)性能极差,HashSet是无序集合,不支持高效的索引访问

修复方案(推荐使用HttpClient)

WebClient是.NET Framework时代的旧API,推荐使用HttpClient(支持连接池复用、异步编程更友好),以下是修复后的异步代码:

public async Task<HashSet<string>> DownloadWebpagesAsync(HashSet<string> urls)
{
    var htmlSet = new HashSet<string>();
    // 复用HttpClient,避免频繁创建释放连接
    using var httpClient = new HttpClient();
    // 收集所有下载任务
    var downloadTasks = urls.Select(async url =>
    {
        try
        {
            // 异步获取网页内容
            var htmlContent = await httpClient.GetStringAsync(url);
            // 线程安全地添加到集合
            lock (htmlSet)
            {
                htmlSet.Add(htmlContent);
            }
        }
        catch (Exception ex)
        {
            // 记录错误,方便排查具体URL的问题
            Console.WriteLine($"下载URL [{url}] 失败: {ex.Message}");
        }
    });

    // 等待所有下载任务完成
    await Task.WhenAll(downloadTasks);
    return htmlSet;
}

方案优势

  • 用async/await替代回调,异步流程更清晰,避免回调地狱
  • 复用HttpClient,通过连接池提升下载效率
  • 等待所有任务完成后再返回集合,确保结果完整
  • 捕获并记录具体异常,能快速定位哪个URL出了问题
  • 遍历HashSet时直接用Select,避免低效的ElementAt操作

可选:限制并发数(避免过载)

如果URL数量较多,建议限制同时发起的请求数,防止触发服务器限流或本地网络过载,添加SemaphoreSlim实现并发控制:

public async Task<HashSet<string>> DownloadWebpagesAsync(HashSet<string> urls, int maxConcurrency = 5)
{
    var htmlSet = new HashSet<string>();
    using var httpClient = new HttpClient();
    // 限制最大并发请求数
    using var semaphore = new SemaphoreSlim(maxConcurrency);

    var downloadTasks = urls.Select(async url =>
    {
        await semaphore.WaitAsync();
        try
        {
            var htmlContent = await httpClient.GetStringAsync(url);
            lock (htmlSet)
            {
                htmlSet.Add(htmlContent);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"下载URL [{url}] 失败: {ex.Message}");
        }
        finally
        {
            // 释放信号量,允许其他请求执行
            semaphore.Release();
        }
    });

    await Task.WhenAll(downloadTasks);
    return htmlSet;
}

若必须使用WebClient的修复版本

如果项目限制必须用WebClient,可以改用DownloadStringTaskAsync配合async/await,避免回调的闭包问题:

public async Task<HashSet<string>> DownloadWebpagesAsync(HashSet<string> urls)
{
    var htmlSet = new HashSet<string>();
    var taskList = new List<Task>();

    foreach (var url in urls)
    {
        using var client = new WebClient();
        // 异步获取任务,避免回调的闭包陷阱
        var downloadTask = client.DownloadStringTaskAsync(new Uri(url))
            .ContinueWith(task =>
            {
                try
                {
                    if (task.IsCompletedSuccessfully)
                    {
                        lock (htmlSet)
                        {
                            htmlSet.Add(task.Result);
                        }
                    }
                    else if (task.Exception != null)
                    {
                        // 取出内部异常(TargetInvocationException的InnerException才是真实错误)
                        var realException = task.Exception.InnerException ?? task.Exception;
                        Console.WriteLine($"下载URL [{url}] 失败: {realException.Message}");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"处理URL [{url}] 结果失败: {ex.Message}");
                }
            });
        taskList.Add(downloadTask);
    }

    await Task.WhenAll(taskList);
    return htmlSet;
}

使用说明

调用上述方法时,必须用await等待异步操作完成:

var urls = new HashSet<string> { "https://example.com", "https://google.com" };
var result = await DownloadWebpagesAsync(urls);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 18:31:08