为什么在此示例中线程连接的行为有所不同?

时间:2011-06-16 16:27:09

标签: c# multithreading lambda thread-safety

更新的问题更通用:

我有以下代码。当您交换线程[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描述为“不工作”,因为它似乎有时会更新结果数组,有时则不会。它现在似乎被调用了,但是startindex和endindex被破坏了。

3 个答案:

答案 0 :(得分:5)

在启动下一个线程之前,每个线程的第一个代码Join

因此,所有线程都按顺序运行。

第二个代码立即运行所有线程,然后Join同时运行所有线程。

因此,线程在完全相同的数据上并发运行。

第二个代码可能无法正常工作,因为您的代码或数据不是线程安全的。

答案 1 :(得分:1)

你的解决方案是正确的,但你误解了这个问题。

Lambda表达式是线程安全的。

但是,所有lambda表达式都共享相同的i变量 因此,如果其中一个线程在循环移动到下一次迭代后开始启动,它将获取更新的值i

通过在循环中声明一个单独的变量,你强迫每个lambda使用它自己的变量,该变量永远不会改变。

答案 2 :(得分:0)

为了完成我的问题的更新,我深究了它。由于lambda表达式不是线程安全的,我需要在每个循环迭代中将i存储在temp变量中:

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!****
}