C# 中的 foreach
循環與 Lambda 表達式執行緒安全問題
讓我們分析以下程式碼片段:
<code class="language-csharp">foreach (Foo f in ListOfFoo) { Thread thread = new Thread(() => f.DoSomething()); threads.Add(thread); thread.Start(); }</code>
<code class="language-csharp">foreach (Foo f in ListOfFoo) { Foo f2 = f; Thread thread = new Thread(() => f2.DoSomething()); threads.Add(thread); thread.Start(); }</code>
哪個程式碼片段能夠確保每個執行緒都呼叫與執行緒建立時相同的循環迭代中的 Foo
實例上的方法?
在 C# 5 及更高版本中,由於編譯器對閉包作用域內變數定義的更改,這兩個程式碼片段都是安全的。但是,在 C# 5 之前,只有第二個程式碼片段是安全的。
在第一個程式碼片段中,變數 f
在循環外部聲明,並在整個執行過程中保持可見。這表示閉包作用域中只有一個 f
實例。因此,存取 f
的不同執行緒可能會發生衝突,導致方法呼叫錯誤。
為了緩解這個問題,第二個程式碼片段在迴圈內宣告了一個新變數 f2
。這確保了每個閉包作用域都有其對 Foo
實例的參考副本,從而保證了每個執行緒的安全方法執行。
下面的範例示範了這個問題:
<code class="language-csharp">static void Main() { int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach (int i in data) { new Thread(() => Console.WriteLine(i)).Start(); } Console.ReadLine(); }</code>
如果不使用臨時變量,這段程式碼將產生不可預測和不正確的輸出。 這是因為所有執行緒都引用同一個 i
變量,而 foreach
迴圈結束後,i
的值是最後一個值。
因此,為了確保線程安全,在 foreach
循環中使用 Lambda 表達式時,最好始終創建局部變數的副本,如同第二個程式碼片段所示。
以上是我的「foreach」循環和 Lambda 線程在 C# 中安全嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!