2013-08-06 61 views
2

我對多線程編程並不感到滿意,當我試圖在我的代碼中實現它時,遇到了一個異常,我無法找出原因。在任何幫助,這將不勝感激:)
所以,基本上我有該小代碼段:多線程程序中的IndexOutOfRange異常

string[][] Array1 = new string[thread_count][]; 

/* Logic to insert data in Array1 */ 

Thread[] WorkerThreads = new Thread[thread_count]; 

for (int i = 0; i < thread_count; i++) 
{ 
    /* THE EXCEPTION OCCURS IN THE FOLLOWING LINE */ 
    WorkerThreads[i] = new Thread(() => GetVal(Array1[i], val, num)); 
    WorkerThreads[i].Start(); 
} 

for (int i = 0; i < WorkerThreads.Length; i++) 
    WorkerThreads[i].Join(); 

現在,THREAD_COUNT值設置爲10,我正在一個IndexOutOfRange例外。調試器將i的值顯示爲10,而Array1 [10] []是它嘗試訪問的值。
當循環不應該運行那麼遠時,我不明白我的值可以達到10。
任何人都可以指出我哪裏錯了?我正在使用C#。

感謝

回答

3

您有「閉合迴路變量」問題,請檢查:closing over the loop variable considered harmfulThe foreach identifier and closures

添加一個臨時變量來解決這個問題:

for (int i = 0; i < thread_count; i++) 
{ 
    var j = i; 
    /* THE EXCEPTION OCCURS IN THE FOLLOWING LINE */ 
    WorkerThreads[j] = new Thread(() => GetVal(Array1[j], val, num)); 
    WorkerThreads[j].Start(); 
} 
+0

我遵循的關閉都引用相同的變量'i' - 而不是i'有__value__'在那個循環中。但爲什麼創建一個臨時變量'j'從根本上改變綁定/範圍的方式? –

+1

由於「j」在循環內部具有局部範圍,因此會爲循環的每次迭代創建一個新的「j」。每個lambda表達式都有自己的副本。雖然「i」在循環的外部範圍中,所以變量在每次迭代中都被重用,並且每個lambda表達式都獲得相同的變量,並在循環之後重用。 – fcuesta

+0

謝謝@fcuesta - 我可以從邏輯上分析這一點,但我不確定我是否直觀地得到它:) Javascript中出現相同的情況我相信,特別是使用jQuery「處理程序」函數。 –

1

你很可能遇到了一個競爭條件,請嘗試使用ConcurrentBagBlockingCollectionSystem.Collections.Concurrent命名空間中,他們使線程編程更容易。

2

你的問題是你所創建的波長/匿名函數。在循環退出前,變量i的值爲10。當你打電話給new Thread(() => GetVal(Array1[i], val, num));時,你實際上並沒有調用有問題的代碼。匿名函數保持對變量i的引用,並且當您嘗試啓動該線程時,它會查找引用,看到它的值爲10,並且會得到您的異常。

參考這個網頁的部分「在Lambda表達式變量範圍」:http://msdn.microsoft.com/en-us/library/vstudio/bb397687.aspx

1

嘗試下面

for (int i = 0; i < thread_count; i++) 
{ 
    int copy = i; 
    WorkerThreads[copy] = new Thread(() => GetVal(Array1[copy], val, num)); 
    WorkerThreads[copy].Start(); 
} 
0

GetVal(Array[i])導致封閉在所述可變i,我將等於前THREAD_COUNT線程被創建。在這種情況下,所有線程都有GetVal(Array[10]),因爲它們都引用最新的i變量。

要解決這個問題,在GetVal使用之前創建分配給Array[i]一個變量:

for (int i = 0; i < thread_count; i++) 
{ 
    string[] value = Array1[i]; 

    WorkerThreads[i] = new Thread(() => GetVal(value, val, num)); 
    WorkerThreads[i].Start(); 
}