从Parallel.ForEach到多线程

时间:2016-05-31 02:08:03

标签: c# .net multithreading parallel-processing

所以我将递归函数转换为迭代函数,然后使用Parallel.ForEach但是当我通过VTune运行它时,它在大部分运行时间内只使用了2个逻辑核心。

我决定尝试使用托管线程,并转换此代码:

for (int N = 2; N <= length; N <<= 1)
{
    int maxThreads = 4;
    var workGroup = Enumerable.Range(0, maxThreads);

    Parallel.ForEach(workGroup, i =>
    {
        for (int j = ((i / maxThreads) * length); j < (((i + 1) / maxThreads) * length); j += N)
        {
            for (int k = 0; k < N / 2; k++)
            {
                int evenIndex = j + k;
                int oddIndex = j + k + (N / 2);

                var even = output[evenIndex];
                var odd = output[oddIndex];

                output[evenIndex] = even + odd * twiddles[k * (length / N)];
                output[oddIndex] = even + odd * twiddles[(k + (N / 2)) * (length / N)];
            }
        }
    });
}

进入这个:

for (int N = 2; N <= length; N <<= 1)
{
    int maxThreads = 4;

    Thread one = new Thread(() => calculateChunk(0, maxThreads, length, N, output));
    Thread two = new Thread(() => calculateChunk(1, maxThreads, length, N, output));
    Thread three = new Thread(() => calculateChunk(2, maxThreads, length, N, output));
    Thread four = new Thread(() => calculateChunk(3, maxThreads, length, N, output));

    one.Start();
    two.Start();
    three.Start();
    four.Start();
}

public void calculateChunk(int i, int maxThreads, int length, int N, Complex[] output)
{
    for (int j = ((i / maxThreads) * length); j < (((i + 1) / maxThreads) * length); j += N)
    {
        for (int k = 0; k < N / 2; k++)
        {
            int evenIndex = j + k;
            int oddIndex = j + k + (N / 2);
            var even = output[evenIndex];
            var odd = output[oddIndex];

            output[evenIndex] = even + odd * twiddles[k * (length / N)];
            output[oddIndex] = even + odd * twiddles[(k + (N / 2)) * (length / N)];
        }
    }
}

问题出现在N循环的最后一次迭代的第四个线程中我得到索引超出范围的索引异常,其中索引尝试访问等效的length

我无法使用调试查明原因,但我相信这是与线程有关,我运行的代码没有线程,它按预期工作。

如果任何代码需要更改让我知道,我通常会有一些人建议编辑。感谢您的帮助,我尝试自己排序,并且相当确定我的线程中出现问题,但我看不清楚。

PS:预期目的是并行化这段代码。

1 个答案:

答案 0 :(得分:1)

观察到的行为几乎可以肯定是由于使用了捕获的循环迭代变量N。我可以通过简单的测试重现您的情况:

ConcurrentBag<int> numbers = new ConcurrentBag<int>();

for (int i = 0; i < 10000; i++)
{
    Thread t = new Thread(() => numbers.Add(i));

    t.Start();
    //t.Join(); // Uncomment this to get expected behaviour.
}

// You'd not expect this assert to be true, but most of the time it will be.
Assert.True(numbers.Contains(10000));

简单地说,在执行for调用的代理可以复制N的值之前,N循环正在竞争增加calculateChunk。因此,calculateChunk会看到N几乎随机的值(包括)length <<= 1 - 这就是导致IndexOutOfRangeException的原因。

您将获得的输出值也将是垃圾,因为您永远不能依赖N正确的值。

如果您希望安全地重写原始代码以使用更多内核,请将Parallel.ForEach从内部循环移动到外部循环。如果外循环迭代次数很多,那么负载均衡器将能够正常工作(当前workGroup计数为4时不能正常工作 - 元素数量太少)。 / p>