2012-03-16 15 views
10

Ho un'applicazione che effettua un paio di centinaia di connessioni TCP contemporaneamente e riceve da loro un flusso costante di dati.Sostituzione Socket.ReceiveAsync con NetworkStream.ReadAsync (attendibile)

private void startReceive() 
    { 
     SocketAsyncEventArgs e = new SocketAsyncEventArgs(); 
     e.Completed += receiveCompleted; 
     e.SetBuffer(new byte[1024], 0, 1024); 
     if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); } 
    } 

    void receiveCompleted(object sender, SocketAsyncEventArgs e) 
    { 
     ProcessData(e); 

     if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); } 
    } 

I miei tentativi hanno portato a qualcosa di simile:

private async void StartReceive() 
    { 
     byte[] Buff = new byte[1024]; 
     int recv = 0; 
     while (Socket.Connected) 
     { 
      recv = await NetworkStream.ReadAsync(Buff, 0, 1024); 
      ProcessData(Buff,recv); 
     } 
    } 

Il problema che ho avuto è stato il metodo chiamando StartReceive() avrebbe bloccato, e non arrivare al accompagna StartSend() method called after StartReceive() . Creating a new task for StartReceive() would just end up with 300-ish threads, and it seems to do so just by calling StartReceive() `comunque.

quale sarebbe il metodo corretto di attuazione delle nuove async e await parole sul mio codice esistente mentre si utilizza un NetworkStream quindi sta usando il pool di thread che Socket.SendAsync() e Socket.ReceiveAsync() stanno usando per evitare di dover avere centinaia di fili/compiti?

C'è qualche vantaggio in termini di prestazioni dell'uso di networkstream in questo modo su porte di completamento di I/O con beginreceive?

+0

Non è chiaro quale sia il problema o il contesto: parli del metodo di chiamata che blocca ... cosa blocca? Non è questo il problema che devi risolvere? Per favore chiarisci la tua domanda - oltre a indicare se tutto il "lavoro" reale ha un'affinità di thread (ad es. Nel thread dell'interfaccia utente). –

+0

Beh, il problema principale è che la versione che utilizza attendi NetworkStream.ReadAsync fa sì che l'applicazione crei un thread per connessione. Dove l'uso di Socket.ReceiveAsync sembra passare sotto i 30 thread per tutte le 300 connessioni. – Josh

risposta

25

stai cambiando due cose contemporaneamente qui: lo stile asincrono (SocketAsyncEventArgs a Task/async) e il livello di astrazione (Socket-NetworkStream).

Poiché si è già a proprio agio con Socket, si consiglia di modificare lo stile asincrono e continuare a utilizzare direttamente la classe Socket.

Il CTP asincrono non fornisce Socket alcuno async metodi compatibili (che è strano; presumo che siano stati lasciati fuori per errore e verranno aggiunti in .NET 4.5).

Non è che è difficile creare il proprio ReceiveAsyncTask metodo di estensione (e involucri simili per le altre operazioni) se si utilizza il mio AsyncEx library:

public static Task<int> ReceiveAsyncTask(this Socket socket, 
    byte[] buffer, int offset, int size) 
{ 
    return AsyncFactory<int>.FromApm(socket.BeginReceive, socket.EndReceive, 
     buffer, offset, size, SocketFlags.None); 
} 

Una volta fatto questo, il tuo StartReceive può essere scritto come tale:

private async Task StartReceive() 
{ 
    try 
    { 
    var buffer = new byte[1024]; 
    while (true) 
    { 
     var bytesReceived = await socket.ReceiveAsyncTask(buffer, 0, 1024) 
      .ConfigureAwait(false); 
     ProcessData(buffer, bytesReceived); 
    } 
    } 
    catch (Exception ex) 
    { 
    // Handle errors here 
    } 
} 

Ora, per affrontare molti punti minori:

  • await non genera una nuova discussione. Ho scritto an async/await intro on my blog, come molti altri. async/await consente la concorrenza, ma questo non implica necessariamente il multithreading.
  • Centinaia di thread possono essere problematici. Centinaia di attività , tuttavia, non rappresentano affatto un problema; il pool di thread e BCL sono progettati per gestire molte, molte attività.
  • async/await non è una nuova forma di elaborazione asincrona; è solo una modo più semplice a express elaborazione asincrona. Utilizza ancora IOCP sottostanti.async/await ha prestazioni leggermente inferiori rispetto ai metodi di livello inferiore; il suo fascino è la facilità di scrivere e comporre metodi asincroni.
  • Un sistema molto occupato può vedere un aumento della pressione del GC quando passa a async/await. Stephen Toub sul team parallelo wrote up some example socket-specific awaitables che può aiutare con quel problema. (Io consiglio di utilizzare prima il modello diretto e di usare l'approccio basato sul rendimento solo se lo ritieni necessario, tuttavia è bello sapere che è là fuori se ne hai bisogno).
  • I metodi asincroni devono restituire Task a meno che non sia realmente necessario per restituire void. Task è attendibile, quindi il tuo metodo è componibile (e più facilmente verificabile); void è più simile a "fuoco e dimentica".
  • È possibile chiamare ConfigureAwait(false) per comunicare il resto del metodo async da eseguire su un thread del thread. Uso questo nel mio esempio sopra in modo che ProcessData venga eseguito in un thread pool di thread, proprio come quando si utilizzava SocketAsyncEventArgs.
  • Socket.Connected è inutile. You need to send data to detect if the connection is still valid.
+0

Grazie per molte di queste informazioni utili. E lo so, stavo solo usando Socket.Connected per una veloce scrittura. – Josh

+0

È stato implementato in .Net 4.5? – Isaac

+1

@Isaac: No. Penso che il team BCL abbia deciso che avrebbe portato a troppe API per il tipo 'Socket'. –