2009-03-05 89 views
6

我打算髮佈一個問題,但提前計算出來,並決定發佈問題和答案 - 或者至少我的意見。使用匿名代理與.NET ThreadPool.QueueUserWorkItem

當使用匿名委託作爲WaitCallback,其中ThreadPool.QueueUserWorkItem在foreach循環中被調用時,似乎將相同的foreach-value傳遞給每個線程。

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

對於物聯網16事實例的集合,我觀察到,每一個「東西」傳遞給WorkWithOneThing對應到最後一個項目的「東西」名單。

我懷疑這是因爲委託正在訪問't'外部變量。請注意,我也嘗試將Thing作爲參數傳遞給匿名委託,但行爲仍然不正確。

當我重新考慮使用命名的WaitCallback方法並將Thing't'傳遞給該方法的代碼時,viola ...將第i個事件實例正確傳遞到WorkWithOneThing中。

我猜想我在並行的課程。我也想象一下,Parallel.For家族解決這個問題,但是這個庫對於我們來說不是一個選擇。

希望這可以爲他人節省一些時間。

霍華德·霍夫曼

+0

如果試圖編譯這段代碼,你會不會得到一個錯誤「System.Threading.WaitCallback」不走‘0’參數」您指定的任何PARAM – ram 2010-03-12 20:14:01

+0

拉姆 - 嘗試改變上述聲明的: 委託(){ ... } 到 委託 { ... } 這就是我要在做出我的改變之前。希望這可以幫助你。 – 2010-03-22 19:25:07

回答

7

這是正確的,並介紹了C#如何捕獲內關閉外面的變量。這不是關於並行性的問題,而是關於匿名方法和lambda表達式的問題。

This question討論了此語言功能及其含義的詳細內容。

1

這是使用閉包時常見的現象,在構建LINQ查詢時尤其明顯。閉包引用了變量,而不是它的內容,因此,爲了讓你的例子工作,你可以在循環內指定一個變量,它取值爲t,然後在閉包中引用它。這將確保您的匿名委託的每個版本引用不同的變量。