2010-10-10 59 views
7

我知道.NET lambda表達式可以捕獲外部變量。但是,我已經看到很多次變量被顯式地傳遞給lambda表達式作爲參數,並且.NET庫似乎也支持這個(例如ThreadPool.QueueUserWorkItem)。Lambda表達式,捕獲的變量和線程

我的問題是,這些捕獲的侷限性是什麼?如何在實際上在不同於它們創建的線程(例如ThreadPool.QueueUserWorkItem或Thread)上執行的lambda表達式或充當回調函數(即稍後調用)的lambda?

通常,我應該何時依賴捕獲的變量,以及何時使用顯式參數?例如:

public void DoStuff() 
{ 
    string message = GetMessage(); 

    ThreadPool.QueueUserWorkItem(s => SendMessage(message)); // use captured variable 
    // -- OR -- 
    ThreadPool.QueueUserWorkItem(s => 
      { 
       string msg = (string)s; 
       SendMessage(msg); 
      }, message); // use explicit parameter 
} 

謝謝!

更新:修復了第二個ThreadPool.QueueUserWorkItem示例。

回答

9

我想在你的第一個例子中,你的意思是

QueueUserWorkItem(() => SendMessage(message)); 

在你的第二個項目,其中沒有參數s從何而來?我想你的意思

QueueUserWorkItem(s => {SendMessage((string)s);} , message); 

現在,這兩個都等效工作,但

  • 在第一種情況:該參數 message是從 這個DoStuff方法範圍複製和直接儲存 在你的lambda表達式 本身,作爲一個閉包。拉姆達有 保留message的副本。

  • 在第二種情況:message發送 到Queue,並且隊列保持 保持的它(連同拉姆達) 直到拉姆達被調用。在運行 lambda時,通過了 到lambda。

我認爲第二種情況是更靈活的編程方式,因爲它理論上可以讓你以後改變message參數的值的拉姆達被調用之前。然而,第一種方法更容易閱讀,對副作用更加免疫。但在實踐中,兩者都有效。

+0

你說得對,我錯過了第二部分例子中的參數。據我所知,在實踐中,我使用哪一個並不重要,但是由於簡單性(和可讀性),您會推薦我使用捕獲的變量。非常感謝您的詳細解答! – ShdNx 2010-10-10 12:04:42

5

對於您的示例(對不可變字符串對象的引用),它絕對沒有區別。

而且一般來說,製作參考文件的副本不會有太大的區別。

double value = 0.1; 

ThreadPool.QueueUserWorkItem(() => value = Process(value)); // use captured variable 

// -- OR -- 

ThreadPool.QueueUserWorkItem(() => 
     { 
      double val = value;  // use explicit parameter 
      val = Process(val);  // value is not changed 
     }); 

// -- OR -- 

ThreadPool.QueueUserWorkItem((v) => 
     { 
      double val = (double)v; // explicit var for casting 
      val = Process(val);  // value is not changed 
     }, value); 

第一個版本是不是線程安全的,第二個和第三個可能是:值類型工作時,這非常重要。

最後一個版本使用的參數是object state,它是Fx2(pre-LINQ)功能。

+0

我不得不承認,我發現你的示例代碼令人困惑。在第一部分中,您使用的是參數,但您不會將其傳遞給lambda。但是那不是使用捕獲的變量(根據我的理解),因爲那意味着直接使用值變量。你的第二個例子指出了不可變類型的行爲,這是正確的,但它不能幫助我理解它們之間的差異。你能否更新你的答案來澄清這一點? – ShdNx 2010-10-10 12:09:00

+0

@Shnx,你是對的,破舊的寫作。我會解決它。 – 2010-10-10 12:13:38

+0

謝謝,現在很清楚。唯一的問題是你也應該更新代碼中的註釋。 +1提到threadsafety(雖然它只適用於不可變的類型),但我接受了Sanjay Manohar的回答,因爲它更快,更清晰,更詳細。也感謝你的幫助! – ShdNx 2010-10-10 12:24:15

1
  1. 如果您希望匿名仿函數引用相同的狀態,請從公共上下文中捕獲標識符。
  2. 如果你想要無狀態lambda表達式(正如我在推薦的併發應用程序中)使用本地副本。