2010-05-27 1 views
9

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

+0

Что контекст этой операции? Внутри веб-страницы? – Keltex

+0

Такая вещь почти тривиальна в F #. Возможно, стоит написать модуль в F #, который можно вызвать из кода C# ... –

+0

@Keltex он будет частью веб-приложения. – aepheus

ответ

4

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

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

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

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

+0

В контексте пула потоков IIS этот поток по-прежнему используется или он будет возвращен в пул, как и для асинхронного вызова? – aepheus

3

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

+1

На самом деле, он просто сделает синхронный вызов в отдельном рабочем потоке. Чтобы решить эту проблему, вы должны использовать 'TaskFactory.FromAsync' +' client.BeginGetResponse'. Таким образом, порты завершения ввода-вывода будут использоваться без блокировки потока. – VirusX

Смежные вопросы