C # несколько асинхронных HttpRequest с одним обратным вызовом - PullRequest
9 голосов
/ 27 мая 2010

Я хочу сделать 10 асинхронных HTTP-запросов одновременно и обрабатывать результаты только после того, как все завершено, и в одной функции обратного вызова. Я также не хочу блокировать какие-либо потоки, используя WaitAll (насколько я понимаю, WaitAll блокирует, пока все не будет завершено). Я думаю, что я хочу сделать пользовательский IAsyncResult, который будет обрабатывать несколько вызовов. Я на правильном пути? Есть ли какие-нибудь хорошие ресурсы или примеры, которые описывают обработку этого?

Ответы [ 4 ]

4 голосов
/ 27 мая 2010

Мне нравится решение Дарина. Но, если вы хотите что-то более традиционное, вы можете попробовать это.

Я бы определенно использовал массив дескрипторов ожидания и механизм 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!");
    }
}
3 голосов
/ 27 мая 2010

В .NET 4.0 есть хорошая параллель Библиотека задач , которая позволяет вам делать такие вещи, как:

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 в списке, запускается отдельная задача, и после завершения всех задач вызывается обратный вызов и передается результат всех задач.

1 голос
/ 27 мая 2010

Я думаю, вам лучше использовать подход WaitAll. В противном случае вы будете обрабатывать 10 обратных вызовов IAsyncResult и использовать семафор, чтобы определить, что все 10 завершены.

Имейте в виду, что WaitAll очень эффективен; это не похоже на глупость нить "спать". Когда поток спит, он продолжает использовать время обработки. Когда поток «не запланирован» из-за того, что он ударил WaitAll, тогда поток больше не потребляет процессорное время. Это очень эффективно.

0 голосов
/ 27 мая 2010
...