2009-03-05 12 views
6

Stavo per pubblicare una domanda, ma ho capito prima e ho deciso di pubblicare la domanda e la risposta, o almeno le mie osservazioni.Utilizzo di delegati anonimi con .NET ThreadPool.QueueUserWorkItem

Quando si utilizza un delegato anonimo come WaitCallback, in cui ThreadPool.QueueUserWorkItem viene chiamato in un ciclo foreach, sembra che lo stesso valore foreach venga passato in ogni thread.

List<Thing> things = MyDb.GetTheThings(); 
foreach(Thing t in Things) 
{ 
    localLogger.DebugFormat("About to queue thing [{0}].", t.Id); 
    ThreadPool.QueueUserWorkItem(
     delegate() 
     { 
      try 
      { 
       WorkWithOneThing(t); 
      } 
      finally 
      { 
       Cleanup(); 
       localLogger.DebugFormat("Thing [{0}] has been queued and run by the delegate.", t.Id); 
      } 
     } 
} 

Per una collezione di 16 istanze cosa in cose ho osservato che ogni 'cosa' passato a WorkWithOneThing corrisponde l'ultimo elemento della lista 'cose'.

Sospetto che ciò accada perché il delegato accede alla variabile esterna "t". Nota che ho anche provato a passare la cosa come parametro al delegato anonimo, ma il comportamento è rimasto scorretto.

Quando ho rielaborato il codice per utilizzare un metodo WaitCallback chiamato e passato la cosa 't' al metodo, viola ... l'istanza di I'thth è stata passata correttamente in WorkWithOneThing.

Una lezione di parallelismo, immagino. Immagino anche che il Parallel.Per la famiglia affronta questo, ma quella biblioteca non era un'opzione per noi a questo punto.

Spero che questo salvi qualcun altro un po 'di tempo.

Howard Hoffman

+0

Se si tenta di compilare questo codice, non si ottiene un errore "System.Threading.WaitCallback' non tiene 'argomenti 0'", come si specifica alcun parametro – ram

+0

Ram - Prova a cambiare la dichiarazione di cui sopra da: delegato() { ...} a delegato { ...} Questo è quello che avevo essere prima di fare il mio cambiamento. Spero che questo ti aiuti. –

risposta

7

Questo è corretto, e descrive come C# cattura variabili al di fuori all'interno di chiusure. Non è direttamente un problema di parallelismo, ma piuttosto di metodi anonimi e espressioni lambda.

This question discute questa funzionalità linguistica e le sue implicazioni in dettaglio.

+0

Anche questo articolo è stato utile: http://www.managed-world.com/archive/2008/06/13/lambdas---know-your-closures.aspx –

1

Questo è un evento comune quando si utilizzano chiusure ed è particolarmente evidente quando si costruiscono query LINQ. La chiusura fa riferimento alla variabile, non al suo contenuto, quindi, per far funzionare il tuo esempio, puoi semplicemente specificare una variabile all'interno del ciclo che prende il valore di t e quindi fa riferimento a quello nella chiusura. Ciò garantirà che ogni versione del tuo delegato anonimo faccia riferimento a una variabile diversa.

Problemi correlati