2010-05-27 24 views
9

Quiero hacer 10 solicitudes http asíncronas a la vez y solo procesar los resultados cuando todos se hayan completado y en una sola función de devolución de llamada. Tampoco quiero bloquear ningún hilo usando WaitAll (tengo entendido que WaitAll bloquea hasta que todos estén completos). Creo que quiero hacer un IAsyncResult personalizado que manejará múltiples llamadas. ¿Estoy en el camino correcto? ¿Existen buenos recursos o ejemplos que describan el manejo de esto?C# HttpRequest asíncrono múltiple con una devolución de llamada

+0

¿Cuál es el contexto de esta operación? Dentro de una página web? – Keltex

+0

Este tipo de cosas es muy trivial en F #. Puede valer la pena escribir un módulo en F # que pueda invocarse desde el código C# ... –

+0

@Keltex sería parte de una aplicación web. – aepheus

Respuesta

4

Me gusta la solución de Darin. Pero, si quieres algo más tradicional, puedes probar esto.

Definitivamente, utilizar una gran variedad de mangos de espera y el mecanismo 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

Creo que es mejor usar el enfoque WaitAll. De lo contrario, procesará 10 devoluciones de llamada IAsyncResult y usará un semáforo para determinar que los 10 finalmente se completaron.

Tenga en cuenta que WaitAll es muy eficiente; no es como la tontería de tener un hilo "dormido". Cuando un hilo duerme, continúa usando el tiempo de procesamiento. Cuando un hilo se "des-programa" porque golpea a un WaitAll, entonces el hilo ya no consume ningún tiempo de procesador. Es muy eficiente

+0

En el contexto del grupo de subprocesos de IIS, ¿el subproceso aún está en uso o se volvería a colocar en el grupo de servidores como lo haría para una llamada asincrónica? – aepheus

3

En .NET 4.0 hay un buen paralelo Task library que le permite hacer cosas como:

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(); 
    } 
} 

Como se puede ver por cada URL en la lista se inicia una tarea diferente y una vez que se hayan completado todas las tareas de la la devolución de llamada se invoca y pasa el resultado de todas las tareas.

+1

En realidad, solo realizará una llamada síncrona en un subproceso independiente. Para resolver esto, debe hacer uso de 'TaskFactory.FromAsync' +' client.BeginGetResponse'. De esta forma, los puertos de finalización de E/S se usarán sin bloqueo de subprocesos. – VirusX

Cuestiones relacionadas