2009-12-11 3 views
10

strano che non ho ancora capito, è questo:Eccezione da espressioni lambda

Say,

try 
{ 
    stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
     SocketFlags.None, ar => stateClient.Socket.EndSend(ar), stateClient); 
} 
catch (SocketException ex) 
{ 
    // Handle SocketException. 
} 
catch (ObjectDisposedException ex) 
{ 
    // Handle ObjectDisposedException. 
} 

Non capisco il motivo per cui l'espressione lambda che restituisce con ObjectDisposedException non e 'colto in !? Stavo andando più a fondo in lambda e non riesco a capirlo. Riguarda lo scopo di lambda? Range Variabili? Problema del filo? So che lambda non ha multi-threading per loro natura ma come puoi vedere il ritorno proviene da un'altra discussione creata da BeginSend. Prima di convertire l'implementazione in una lambda, questo era ok quando avevo un metodo AsyncCallBack che gestiva lo EndSend.

Qualsiasi aiuto apprezzato. Grazie in anticipo.

risposta

17

Si ha ragione che i lamdas non hanno intrinseca asincronismo o built-in multithreading, ma Socket.BeginSend lo fa.

Quello che succede è che il blocco try incapsula la chiamata a BeginSend. Se quella chiamata ha esito positivo, non viene generata alcuna eccezione e viene restituito il metodo di inclusione, indipendentemente da ciò che accade su altri thread.

Se si verifica un'eccezione durante la chiamata a BeginSend, i blocchi di cattura verranno richiamati.

Tuttavia, l'espressione lambda è una richiamata asincrona, quindi non verrà invocata più tardi. Questo accade in un callstack separato su un thread separato, quindi il blocco try non è attivo lì.

Se si desidera la gestione degli errori per il callback, è necessario specificarlo all'interno del callback stesso (ovvero, all'interno del lambda).

+0

Ben spiegato, Mark, grazie .. –

7

Non è correlato a lambda. Il delegato della chiamata BeginSend viene eseguito su un altro thread, pertanto l'eccezione non viene generata sul thread che contiene le istruzioni catch e pertanto non è gestita. Posizionare la gestione delle eccezioni insieme al codice per EndSend.

Per ulteriori informazioni vedere http://msdn.microsoft.com/en-us/library/38dxf7kt.aspx

+0

Questo è quello che prima però, mi stavo chiedendo se potevo evitare questo :) ma ha senso completo ... –

+1

Dire che la chiamata 'BeginSend' eseguita su un altro thread non è un po 'fuorviante? –

+0

Angelo, non so cosa vuoi dire fuorviante. Tutti i metodi Beginxxx sono in esecuzione su un altro thread rispetto a quello "avviato", "chiamato", quei metodi stanno utilizzando la parte Multi I/O della CPU e gestiscono automaticamente il threading. –

1

La chiamata alla funzione anonima definita dalla lambda avviene in modo asincrono. Il blocco di prova sarà ormai lontano da allora.

codice Si è lo stesso: -

AsyncCallBack cb = delegate(AsyncCallback ar) { stateClient.Socket.EndSend(ar); } 
stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
    SocketFlags.None, cb, stateClient); 

Ora si può definire una funzione di: -

void MyCallBack(AsyncCallback ar) { stateClient.Socket.EndSend(ar); } 

e poi il codice di cui sopra potrebbe diventare: -

stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
    SocketFlags.None, MyCallBack, stateClient); 

È praticamente la stessa cosa in questo caso. Il punto è che Try intercetta le eccezioni che si verificano durante l'esecuzione nominale del suo corpo. Il fatto che il codice definito dall'utente all'interno del corpo sotto forma di lambda non lo rende più soggetto al blocco Try come lo MyCallBack precedente. Entrambi verrebbero eseguiti dopo la funzione che contiene il blocco Try o eventualmente durante, ma su un thread diverso.

+0

Effettivamente potrei usare il metodo anonimo invece di un Lambda, grazie per il tuo sforzo :) –

0

Come già affermato in altre risposte, la chiamata al lambda avverrà in modo asincrono e questa è la ragione per cui l'eccezione non viene rilevata.

Un esempio con chiamate asincrone per leggere da un file:

File.WriteAllText("example.txt", new string('0', 2048)); 

Stream s = File.OpenRead("example.txt"); 

var buffer = new byte[1024]; 

Console.WriteLine(
    "Thread: {0} - Before asynch call...", 
    Thread.CurrentThread.ManagedThreadId); 

s.BeginRead(
    buffer, 
    0, 
    1024, 
    ar => 
    { 
     Thread.Sleep(100); // Simulate a long op 
     Console.WriteLine(
      "Thread: {0} - Callback called...", 
      Thread.CurrentThread.ManagedThreadId); 
    } 
    , 0); 

Console.WriteLine(
    "Thread: {0} - After asynch call...", 
    Thread.CurrentThread.ManagedThreadId); 

// Wait for callback to be executed 
Thread.Sleep(2000); 

Il risultato sarebbe:

Thread: 1 - Before asynch call... 
Thread: 1 - After asynch call... 
Thread: 3 - Callback called... 
0

Per quanto penso che ho ragione fino ad ora, non potrà mai tornare BeginSend Un'eccezione, tutte le eccezioni e il risultato sono rerurnati nel metodo EndSend(), quindi è possibile spostare i miei blocchi catch try.

+0

Tranne il caso se (Socket === null) allora BeginSend ti dà una NullReferenceException –