2010-05-27 9 views
9

Chcę wykonać 10 asynchronicznych żądań http na raz i przetwarzać wyniki tylko wtedy, gdy wszystkie zostały zakończone i w jednej funkcji wywołania zwrotnego. Nie chcę również blokować żadnych wątków za pomocą WaitAll (rozumiem, że WaitAll blokuje dopóki wszystkie nie zostaną zakończone). Myślę, że chcę utworzyć niestandardową funkcję IAsyncResult, która będzie obsługiwać wiele połączeń. Czy jestem na dobrej drodze? Czy istnieją jakieś dobre zasoby lub przykłady, które opisują obsługę tego?C# wiele asynchronicznych HttpRequest z jednym oddzwanianiem

+0

Jaki jest kontekst tej operacji? Wewnątrz strony internetowej? – Keltex

+0

Tego typu rzeczy są bardzo banalne w języku F #. Warto napisać moduł w języku F #, który można wywołać z kodu C# ... –

+0

@Keltex byłby częścią aplikacji internetowej. – aepheus

Odpowiedz

4

Podoba mi się rozwiązanie Darina. Ale jeśli chcesz czegoś bardziej tradycyjnego, możesz spróbować tego.

Zdecydowanie używać tablicy uchwytów poczekać i mechanizm 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

myślę, że jesteś lepiej wyłączyć za pomocą podejścia WaitAll. W przeciwnym razie przetwarzasz 10 wywołań zwrotnych IAsyncResult i używasz semafora do określenia, że ​​wszystkie 10 są w końcu ukończone.

Należy pamiętać, że funkcja WaitAll jest bardzo wydajna; to nie jest jak głupota posiadania wątku "spać". Gdy wątek śpi, nadal używa czasu przetwarzania. Kiedy wątek jest "nieplanowany", ponieważ trafił na WaitAll, wątek nie zużywa już czasu procesora. Jest bardzo wydajny.

+0

W kontekście puli wątków IIS, wątek jest nadal w użyciu lub zostałby przywrócony do puli, tak jak w przypadku wywołania asynchronicznego? – aepheus

3

W .NET 4.0 jest ładny równolegle Task library że pozwala robić takie rzeczy jak:

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

Jak widać dla każdego adresu URL z listy inny zadaniem jest uruchamiany i gdy wszystkie zadania są uzupełnione wywołanie zwrotne jest wywoływane i przekazuje wynik wszystkich zadań.

+1

W rzeczywistości wywoła ona synchroniczne wywołanie w osobnym wątku roboczym. Aby rozwiązać ten problem, powinieneś użyć 'TaskFactory.FromAsync' +' client.BeginGetResponse'. W ten sposób zostaną użyte porty ukończenia operacji we/wy bez blokowania wątków. – VirusX

Powiązane problemy