2009-03-05 8 views
6

J'allais poser une question, mais je l'ai compris à l'avance et j'ai décidé d'afficher la question et la réponse - ou du moins mes observations. Lorsque vous utilisez un délégué anonyme en tant que WaitCallback, où ThreadPool.QueueUserWorkItem est appelé dans une boucle foreach, il semble que le même foreach-value soit transmis à chaque thread.Utilisation de délégués anonymes avec .NET ThreadPool.QueueUserWorkItem

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

Pour une collection de 16 instances de chose choses que je remarquai que chaque « chose » est passé à WorkWithOneThing correspond au dernier élément de la liste des « choses ».

Je suppose que c'est parce que le délégué accède à la variable externe 't'. Notez que j'ai également expérimenté en passant le Thing comme paramètre au délégué anonyme, mais le comportement est resté incorrect.

Lorsque j'ai recréé le code pour utiliser une méthode nommée WaitCallback et que j'ai passé le Thing 't' à la méthode, alto ... la iième instance de Things a été correctement passée dans WorkWithOneThing.

Une leçon de parallélisme je suppose. J'imagine aussi que la famille Parallel.For répond à cela, mais cette bibliothèque n'était pas une option pour nous à ce stade.

Espérons que cela sauvera quelqu'un d'autre.

Howard Hoffman

+0

Si vous essayez de compiler ce code, ne vous obtiendrez une erreur « System.Threading.WaitCallback » ne prend pas les arguments 0 ' » comme vous ne spécifiez pas – ram

+0

Ram param - Essayez de changer la déclaration ci-dessus de: délégué() { ...} à délégué { ...} C'est ce que je devais être avant de faire mon changement. J'espère que cela vous aide. –

Répondre

7

Ceci est correct, et décrit comment C# capture des variables extérieures à l'intérieur des fermetures. Ce n'est pas directement un problème sur le parallélisme, mais plutôt sur les méthodes anonymes et les expressions lambda.

This question traite de cette caractéristique du langage et de ses implications en détail.

+0

Cet article vous a également été utile: http://www.managed-world.com/archive/2008/06/13/lambdas---know-your-closures.aspx –

1

Il s'agit d'une occurrence fréquente lors de l'utilisation de fermetures et elle est particulièrement évidente lors de la construction de requêtes LINQ. La fermeture fait référence à la variable, pas à son contenu. Par conséquent, pour que votre exemple fonctionne, vous pouvez simplement spécifier une variable dans la boucle qui prend la valeur de t, puis la référencer dans la fermeture. Cela garantira que chaque version de votre délégué anonyme référence une variable différente.

Questions connexes