2010-05-27 10 views
9

Ich möchte 10 asynchrone HTTP-Anforderungen auf einmal und nur die Ergebnisse verarbeiten, wenn alle abgeschlossen haben und in einer einzigen Callback-Funktion. Ich möchte auch keine Threads mit WaitAll blockieren (es versteht sich, dass WaitAll blockiert, bis alle abgeschlossen sind). Ich denke, ich möchte ein benutzerdefiniertes IAsyncResult machen, das mehrere Anrufe behandelt. Bin ich auf dem richtigen Weg? Gibt es irgendwelche guten Ressourcen oder Beispiele, die den Umgang damit beschreiben?C# mehrere asynchrone HttpRequest mit einem Callback

+0

Was ist der Kontext dieser Operation? Innerhalb einer Webseite? – Keltex

+0

Diese Art von Sache ist in F # fast trivial. Es könnte sich lohnen, ein Modul in F # zu schreiben, das aus C# -Code aufgerufen werden kann ... –

+0

@Keltex es wäre Teil einer Webanwendung. – aepheus

Antwort

4

Ich mag Diess Lösung. Aber, wenn Sie etwas traditionelleres wollen, können Sie das versuchen.

Ich würde auf jeden Fall eine Reihe von Wartegriffen und den WaitAll Mechanismus verwenden:

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

Ich glaube, Sie besser dran sind die WaitAll Ansatz. Andernfalls werden Sie 10 IAsyncResult-Rückrufe verarbeiten und mithilfe eines Semaphors feststellen, dass alle 10 schließlich vollständig sind.

Beachten Sie, dass WaitAll sehr effizient ist; es ist nicht wie die Albernheit, einen Faden "schlafen" zu haben. Wenn ein Thread schläft, verbraucht er weiterhin Verarbeitungszeit. Wenn ein Thread "entplant" wird, weil er einen WaitAll-Wert erreicht hat, verbraucht der Thread keine Prozessorzeit mehr. Es ist sehr effizient.

+0

Wird der Thread im Kontext des IIS-Thread-Pools noch verwendet oder wird er wie für einen asynchronen Aufruf in den Pool zurückgelegt? – aepheus

3

In .NET 4.0 gibt es eine schöne Parallele Task library, dass Sie Dinge wie tun können:

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

Wie Sie für jede URL in der Liste eine andere Aufgabe und einmal gestartet sehen können alle Aufgaben abgeschlossen sind die Callback wird aufgerufen und das Ergebnis aller Aufgaben übergeben.

+1

Eigentlich wird nur synchrone Aufruf in separaten Worker-Thread. Um dies zu lösen, sollten Sie 'TaskFactory.FromAsync' und' client.BeginGetResponse' verwenden. Auf diese Weise werden die E/A-Abschluss-Ports ohne Thread-Blockierung verwendet. – VirusX