2011-06-16 80 views
2

更新的問題更通用:爲什麼在這個例子中線程連接的行爲有所不同?

我有以下代碼。當你交換線程[i] .Join()的位置時,你會得到不同的輸出。

static void ThreadedWorker(int startIndex, int endIndex) 
{ 
    Console.WriteLine("Working from results[ " + startIndex +"] to results["+endIndex+"]"); 
} 

static void Main(string[] args) 
{ 
    int threadCount = System.Environment.ProcessorCount; 
    int calculationCount = 500; //the number of array elements we'd be iterating over if we were doing our work 
    int threadDataChunkSize = calculationCount/threadCount; 
    if (threadDataChunkSize < 1) threadDataChunkSize = 1; //just in case we have loads of threads 

    Thread[] threads = new Thread[threadCount]; 
    for (int i = 0; i < threadCount; i++) 
    { 
     threads[i] = new Thread(() => ThreadedWorker(threadDataChunkSize * i, threadDataChunkSize*(i+1))); 
     threads[i].Start(); 
     //threads[i].Join(); //****Uncomment for correct behaviour**** 
    } 

    for (int i = 0; i < threadCount; i++) 
    { 
     //threads[i].Join(); //****Uncomment for incorrect behaviour**** 
    } 

    Console.WriteLine("breakhere"); 
} 

Join()在第一循環中,創造連續的行爲,你得到的輸出

Working from results[ 0] to results[125] 
Working from results[ 125] to results[250] 
Working from results[ 250] to results[375] 
Working from results[ 375] to results[500] 

Join()是在第二循環中,建立一個平行的行爲,你會得到不確定性的輸出是這樣的:

Working from results[ 375] to results[500] 
Working from results[ 375] to results[500] 
Working from results[ 500] to results[625] 
Working from results[ 500] to results[625] (i is sometimes more than it should ever be!) 

我懷疑lambda表達式以某種方式導致問題。希望這個改寫也表明這不是一個界限的錯誤計算,或者其他濫用我的數組!


最初的問題不是通用的,而是使用startIndex和endIndex遍歷一個正在工作的字節數組。我將ThreadedWorker描述爲「不工作」,因爲它似乎有時會更新結果數組,有時不會。現在看來,它被稱爲,但起始索引和endindex被打破。

+3

定義「不起作用」。 – 2011-06-16 16:28:51

+0

我的輸出數組分成8個塊。前1/8似乎永遠不會被填滿,而較低的條紋有時會被填滿,有時不會。這是全部或沒有,所討論的大塊要麼完全正確,要麼完全錯誤。這使我相信'ThreadedBlend'永遠不會被調用。 – 2011-06-16 17:48:38

回答

1

你的解決方案是正確的,但你誤解了問題。

Lambda表達式是線程安全的。

但是,所有的lambda表達式都共享相同的i變量。
因此,如果其中一個線程恰好在循環移動到下一次迭代之後開始,它將拾取較新的值i

通過在循環中聲明一個單獨的變量,你迫使每個lambda使用自己的變量,它永遠不會改變。

+0

謝謝你的措辭,這個問題好多了!這正是我想表達的,但是對於lambda表達式我沒有真正的知識,我無法在我的解決方案中對其進行描述。我現在要去了解他們... – 2011-06-17 12:32:18

5

第一個代碼Join s在啓動它之後,在開始下一個線程之前。

因此,所有的線程按順序運行。

第二個代碼立即運行所有的線程,然後Join所有的一次。

因此,線程同​​時運行在完全相同的數據上。

第二個代碼可能不起作用,因爲您的代碼或數據不是線程安全的。

+0

是的,我知道第一個順序運行(這就是爲什麼它對我沒有任何用處!)我應該澄清 - 數據包含工作數據的開始和結束點。數組大小t被分成workSize ='t/NumberOfProcessors'塊,這些塊被分配給每個線程。每個線程都從'workSize * i'工作到'workSize *(i + 1)'。因此,對於4個內核的大小爲20的數組,每個塊大小爲5,即thread1(0,5),thread2(5,10),thread3(10,15),thread4(15,20) '。 [不是NumberofProcessors的倍數的數組通過ThreadedBlend中的條件檢查保持安全] – 2011-06-16 17:44:48

+0

@Bom:No;他們都共享相同的「數據」。 – SLaks 2011-06-16 17:48:44

+0

@SLaks我想你知道答案,我不明白...對不起,如果我很慢。我知道它們在同一個數組上運行,但我認爲每個線程可以同時在一個數組的不同細分上運行。這不可能嗎?我的測試用例中的所有線程實際上都在執行相同的操作循環,用128填充數組。訪問數組時是否存在隱式鎖定? – 2011-06-16 19:45:41

0

爲了完成我的問題的更新,我深入瞭解它的底部。隨着lambda表達式不是線程安全的,我需要我存儲在每個循環迭代一個臨時變量:

for (int i = 0; i < threadCount; i++) 
{ 
    int temp = i; 
    threads[temp] = new Thread(() => ThreadedMultiplier(threadDataChunkSize * temp, threadDataChunkSize * (temp + 1))); 
threads[temp].Start(); 
} 

for (int i = 0; i < threadCount; i++) 
{ 
    //threads[i].Join(); //****Uncomment for correct + parallel behaviour at last!**** 
} 
+0

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – SLaks 2011-06-17 12:02:45

相關問題