2011-06-16 70 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

相关问题