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

使用HttpWebRequest.BeginGetResponse发送大量请求时部分未完成

问题排查与修复方案

嘿,我来帮你拆解下这个问题:你发起10000个异步HttpWebRequest,但最终完成计数总是少几十个,核心原因是线程安全问题,另外代码里还有个容易忽略的资源泄漏隐患,咱们一步步来解决。

为什么计数会“丢失”?

你定义的completed变量是在多个异步回调线程中被直接递增的(completed++),这个操作不是原子性的——当多个线程同时读取、修改、写回这个值时,会出现值覆盖的情况。比如线程A和线程B同时读到completed=500,各自加1后写回,结果最终completed变成501而不是502,这就直接导致了计数丢失。

另外,你的代码里用callback.BeginInvoke(...)但从未调用EndInvoke,这会导致异步委托的资源无法被正确释放,长期运行会造成内存泄漏,不过这不是计数缺失的直接原因,但必须修复。

修复步骤

1. 用原子操作保证计数器线程安全

把直接的completed++换成Interlocked.Increment,它能保证递增操作是原子性的,不会被其他线程打断:

var callback = new Action<String, Exception>((r, ex) => 
{ 
    // 用Interlocked.Increment原子递增,返回递增后的当前值
    int currentCount = Interlocked.Increment(ref completed);
    Console.WriteLine(currentCount); 
});

2. 重构WebHelper,避免Begin/End模式的资源泄漏

原来的WebHelper用了嵌套的BeginInvoke,还没处理EndInvoke,这是个隐患。更推荐用现代的async/await来重构,代码更简洁且自动处理资源:

public static class WebHelper
{
    // 异步版本的方法,用async/await替代Begin/End
    private static async Task<string> GetResponseStringAsync(WebRequest request)
    {
        using (var resp = await request.GetResponseAsync())
        using (var sr = new StreamReader(resp.GetResponseStream()))
        {
            return await sr.ReadToEndAsync();
        }
    }

    public static async Task ProcessRequestAsync(WebRequest request, Action<string, Exception> callback)
    {
        try
        {
            var response = await GetResponseStringAsync(request);
            callback(response, null);
        }
        catch (Exception ex)
        {
            callback(null, ex);
        }
    }

    // 保留原来的BeginGetResponse方法,包装异步逻辑
    public static void BeginGetResponse(WebRequest request, Action<string, Exception> callback)
    {
        // 用_忽略返回的Task,避免编译器警告
        _ = ProcessRequestAsync(request, callback);
    }
}

3. 完整的TestingForm修改后代码

public partial class TestingForm : Form 
{ 
    public TestingForm() 
    { 
        InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
        int completed = 0;
        var callback = new Action<String, Exception>((r, ex) => 
        { 
            int currentCount = Interlocked.Increment(ref completed);
            Console.WriteLine(currentCount); 
        }); 

        for (int i = 0; i < 10000; i++) 
        { 
            var req = HttpWebRequest.Create("http://example.com"); 
            req.Proxy = new WebProxy("127.0.0.2:8888"); 
            WebHelper.BeginGetResponse(req, callback); 
        } 
    } 
}

额外注意点

如果你的代理127.0.0.2:8888偶尔不可用或者请求超时,会不会影响计数?其实不会——因为你的代码里已经捕获了所有异常并调用了callback,所以哪怕请求失败,计数器还是会递增。所以修复线程安全问题后,最终计数应该能准确达到10000。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:07:52