2009-03-05 10 views
6

Zamierzałem zadać pytanie, ale zorientowałem się z wyprzedzeniem i postanowiłem zamieścić pytanie i odpowiedź - lub przynajmniej moje spostrzeżenia.Używanie Anonimowych Delegatów z .NET ThreadPool.QueueUserWorkItem

Podczas używania anonimowego uczestnika jako WaitCallback, gdzie ThreadPool.QueueUserWorkItem jest wywoływana w pętli foreach, wydaje się, że ta sama jedna wartość foreach jest przekazywana do każdego wątku.

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

Dla kolekcji 16 przypadkach rzeczą Czego zauważyć, że każda „rzecz” przeszedł do WorkWithOneThing odzwierciedlał ostatniej pozycji na liście "rzeczy.

Podejrzewam, że dzieje się tak, ponieważ delegat uzyskuje dostęp do zmiennej zewnętrznej "t". Zauważ, że eksperymentowałem również z przekazywaniem rzeczy jako parametrem dla anonimowego uczestnika, ale zachowanie pozostało nieprawidłowe.

Kiedy ponownie zakodowałem kod, aby użyć nazwanej metody WaitCallback i przekazałem Thing 't' do metody, viola ... Ithth of Things został poprawnie przekazany do WorkWithOneThing.

Przypuszczam, że to lekcja równoległości. Wyobrażam sobie również, że Parallel.For dla rodziny odnosi się do tego, ale ta biblioteka nie była dla nas opcją w tym momencie.

Mam nadzieję, że to oszczędza komuś innemu czas.

Howard Hoffman

+0

Jeśli spróbujesz skompilować ten kod, nie dostaniesz błędzie „System.Threading.WaitCallback” nie bierze «argumenty 0»”jako określenie nie param – ram

+0

Ram - Spróbuj zmienić powyższe oświadczenie: delegat() { ... } do delegat { ... } to właśnie miałem być przed dokonaniem mojej zmiany. Mam nadzieję, że to ci pomoże. –

Odpowiedz

7

Jest to poprawne i opisuje jak C# przechwytuje zmienne Wewnątrz zamknięć. Nie jest to bezpośrednio kwestia równoległości, ale raczej anonimowych metod i wyrażeń lambda.

This question omawia szczegółowo tę cechę języka i jego konsekwencje.

+0

Ten artykuł również był pomocny: http://www.managed-world.com/archive/2008/06/13/lambdas---now-your-closures.aspx –

1

Jest to powszechne zjawisko podczas używania zamknięć i jest szczególnie widoczne podczas konstruowania zapytań LINQ. W związku z tym zamknięcie odwołuje się do zmiennej, a nie do jej zawartości, aby twój przykład zadziałał, można po prostu podać zmienną wewnątrz pętli, która przyjmuje wartość t, a następnie odwołać się do tej w zamknięciu. To zapewni, że każda wersja twojego anonimowego delegata odwoła się do innej zmiennej.

Powiązane problemy