2010-05-27 6 views
9

Je souhaite effectuer 10 requêtes HTTP asynchrones en même temps et ne traiter les résultats que lorsque tous sont terminés et dans une seule fonction de rappel. Je ne veux pas non plus bloquer les threads en utilisant WaitAll (je crois comprendre que WaitAll bloque jusqu'à ce que tous soient complets). Je pense que je veux faire un IAsyncResult personnalisé qui va gérer plusieurs appels. Suis-je sur la bonne voie? Y a-t-il de bonnes ressources ou des exemples qui décrivent comment gérer cela?HttpRequest asynchrone C# multiple avec un rappel

+0

Quel est le contexte de cette opération? Dans une page Web? – Keltex

+0

Ce genre de chose est presque trivial en F #. Il pourrait être utile d'écrire un module en F # qui peut être appelé à partir du code C# ... –

+0

@Keltex cela ferait partie d'une application web. – aepheus

Répondre

4

J'aime la solution de Darin. Mais, si vous voulez quelque chose de plus traditionnel, vous pouvez essayer cela.

Je certainement utiliser un tableau de poignées d'attente et le mécanisme 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

Je pense que vous feriez mieux d'utiliser l'approche WaitAll. Sinon, vous allez traiter 10 callbacks IAsyncResult et utiliser un sémaphore pour déterminer que tous les 10 sont enfin terminés. Gardez à l'esprit que WaitAll est très efficace; ce n'est pas comme la bêtise d'avoir un fil "dormir". Lorsqu'un thread dort, il continue à utiliser le temps de traitement. Lorsqu'un thread est "dé-planifié" parce qu'il a atteint WaitAll, le thread ne consomme plus de temps processeur. C'est très efficace.

+0

Dans le contexte du pool de threads IIS, le thread est-il toujours en cours d'utilisation ou sera-t-il remis dans le pool comme s'il s'agissait d'un appel asynchrone? – aepheus

3

Dans .NET 4.0 il y a une belle parallèle Task library qui vous permet de faire des choses comme:

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

Comme vous pouvez le voir pour chaque URL dans la liste une tâche différente démarre et une fois que toutes les tâches sont terminées le le rappel est appelé et transmis le résultat de toutes les tâches.

+1

En fait, il va juste faire un appel synchrone dans un thread de travail séparé. Pour résoudre ce problème, vous devez utiliser 'TaskFactory.FromAsync' +' client.BeginGetResponse'. De cette façon, les ports d'achèvement d'E/S seront utilisés sans blocage de thread. – VirusX