【问题标题】:C# multiple asynchronous HttpRequest with one callback一个回调的 C# 多个异步 HttpRequest
【发布时间】:2010-05-27 16:42:16
【问题描述】:

我想一次发出 10 个异步 http 请求,并且只在所有请求都完成后并在一个回调函数中处理结果。我也不想使用 WaitAll 阻塞任何线程(我的理解是 WaitAll 阻塞直到所有线程都完成)。我想我想制作一个自定义的 IAsyncResult 来处理多个调用。我在正确的轨道上吗?有没有好的资源或例子来描述处理这个问题?

【问题讨论】:

  • 这个操作的上下文是什么?在网页内?
  • 这种事情在 F# 中几乎是微不足道的。用 F# 编写一个可以从 C# 代码调用的模块可能是值得的……
  • @Keltex 它将成为 Web 应用程序的一部分。
  • 你说的是从多个网页请求数据吗?
  • @Scott P 我正在调用多个 Web 服务函数。在将结果返回给用户之前,我需要所有结果。

标签: c# asynchronous httprequest


【解决方案1】:

我喜欢达林的解决方案。但是,如果你想要更传统的东西,你可以试试这个。

我肯定会使用等待句柄数组和 WaitAll 机制:

static void Main(string[] args)
{

    WaitCallback del = state =>
    {
        ManualResetEvent[] resetEvents = new ManualResetEvent[10];
        WebClient[] clients = new WebClient[10];

        Console.WriteLine("Starting requests");
        for (int index = 0; index < 10; index++)
        {
            resetEvents[index] = new ManualResetEvent(false);
            clients[index] = new WebClient();

            clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);

            clients[index].OpenReadAsync(new Uri(@"http:\\www.google.com"), resetEvents[index]);
        }

        bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000);
        Complete(succeeded);

        for (int index = 0; index < 10; index++)
        {
            resetEvents[index].Dispose();
            clients[index].Dispose();
        }
    };

    ThreadPool.QueueUserWorkItem(del);

    Console.WriteLine("Waiting...");
    Console.ReadKey();
}

static void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    // Do something with data...Then close the stream
    e.Result.Close();

    ManualResetEvent readCompletedEvent = (ManualResetEvent)e.UserState;
    readCompletedEvent.Set();
    Console.WriteLine("Received callback");
}


static void Complete(bool succeeded)
{
    if (succeeded)
    {
        Console.WriteLine("Yeah!");
    }
    else
    {
        Console.WriteLine("Boohoo!");
    }
}

【讨论】:

    【解决方案2】:

    在 .NET 4.0 中有一个很好的并行 Task library 允许您执行以下操作:

    using System;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    
    class Program
    {
        public static void Main()
        {
            var urls = new[] { "http://www.google.com", "http://www.yahoo.com" };
    
            Task.Factory.ContinueWhenAll(
                urls.Select(url => Task.Factory.StartNew(u => 
                {
                    using (var client = new WebClient())
                    {
                        return client.DownloadString((string)u);
                    }
                }, url)).ToArray(), 
                tasks =>
                {
                    var results = tasks.Select(t => t.Result);
                    foreach (var html in results)
                    {
                        Console.WriteLine(html);
                    }
            });
            Console.ReadLine();
        }
    }
    

    如您所见,列表中的每个 url 都会启动一个不同的任务,一旦所有任务完成,就会调用回调并传递所有任务的结果。

    【讨论】:

    • 实际上,它只是在单独的工作线程中进行同步调用。为了解决这个问题,你应该使用TaskFactory.FromAsync + client.BeginGetResponse。这样,I/O 完成端口将在没有线程阻塞的情况下使用。
    【解决方案3】:

    我认为您最好使用 WaitAll 方法。否则,您将处理 10 个 IAsyncResult 回调,并使用信号量来确定所有 10 个最终完成。

    请记住,WaitAll 非常高效;这不像让线程“睡觉”的愚蠢。当线程休眠时,它会继续使用处理时间。当一个线程因为遇到 WaitAll 而被“取消调度”时,该线程不再消耗任何处理器时间。效率很高。

    【讨论】:

    • 在 IIS 线程池的上下文中,是线程仍在使用还是会像异步调用一样被放回池中?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 2020-12-29
    相关资源
    最近更新 更多