关于多线程,锁和多核处理器的多部分问题(多^ 3)

时间:2010-05-31 04:34:17

标签: c# .net multithreading locking multicore

我有一个有两种方法的程序。第一种方法将两个数组作为参数,并执行一个操作,其中来自一个数组的值有条件地写入另一个数组,如下所示:

void Blend(int[] dest, int[] src, int offset)
{
    for (int i = 0; i < src.Length; i++)
    {
        int rdr = dest[i + offset];
        dest[i + offset] = src[i] > rdr? src[i] : rdr; 
    }
}

第二种方法创建两组独立的int数组,并对它们进行迭代,使得一组中的每个数组都与另一组中的每个数组进行Blend编辑,如下所示:

void CrossBlend()
{
    int[][] set1 = new int[150][75000]; // we'll pretend this actually compiles
    int[][] set2 = new int[25][10000]; // we'll pretend this actually compiles
    for (int i1 = 0; i1 < set1.Length; i1++)
    {
        for (int i2 = 0; i2 < set2.Length; i2++)
        {
            Blend(set1[i1], set2[i2], 0); // or any offset, doesn't matter
        }
    }
}

第一个问题:由于这个apporoach显然是并行化的候选者,它本质上是线程安全的吗?似乎没有,因为我可以设想一个场景(我认为不太可能),因为不同的线程〜同时操作,一个线程的更改会丢失。

如果不是,请这样:

void Blend(int[] dest, int[] src, int offset)
{
    lock (dest)
    {
        for (int i = 0; i < src.Length; i++)
        {
            int rdr = dest[i + offset];
            dest[i + offset] = src[i] > rdr? src[i] : rdr; 
        }
    }
}

是一个有效的解决方案吗?

第二个问题:如果是这样,使用这样的锁可能会有什么性能成本?我假设有这样的事情,如果一个线程试图锁定当前被另一个线程锁定的目标数组,第一个线程将阻塞,直到锁被释放而不是继续处理某些东西。

此外,获取锁定需要多长时间?纳秒级,还是比那更差?这会是一个像这样的主要问题吗?

第三个问题:我将如何以多线程方式最好地解决这个问题,以利用多核处理器(这是基于多线程的潜在错误假设解决方案不会加速单核处理器上的这个操作)?我猜我想要每个核心运行一个线程,但我不知道这是不是真的。

1 个答案:

答案 0 :(得分:3)

CrossBlend的潜在争用是set1 - 混合的目标。而不是使用与您正在进行的工作量相比相对昂贵的锁,安排每个线程在其自己的目的地上工作。这是给定目标(set1中某个索引处的数组)由给定任务拥有。这是可能的,因为结果与CrossBlend处理数组的顺序无关。

然后每个任务应该只运行CrossBlend中的内部循环,并且使用要使用的dest数组(set1)的索引(或索引范围)来参数化任务。

您还可以并行化Blend方法,因为每个索引都是独立于其他索引计算的,因此不存在争用。但是在今天的机器上,使用&lt; 40核心,你将获得足够的并行性,只需穿过CrossBlend方法。

要在多核上有效运行,您可以

  1. 对于N个核心,将问题分成N个部分。鉴于set1与核心数量相比相当大,您可以将set1划分为N个范围,并将每个索引范围传递给运行内部CrossBlend循环的N个线程。这将给你相当好的并行性,但它不是最优的。 (有些线程会很快完成,但最终没有工作要做。)
  2. 更复杂的方案是使CrossBlend内循环的每次迭代成为一个单独的任务。有N个队列(用于N个核心),并在队列中分配任务。启动N个线程,每个线程从队列中读取它的任务。如果线程队列变空,则从其他线程的队列中获取任务。
  3. 第二种方法最适合于规模不规则的任务,或者系统用于其他任务的情况,因此某些核心可能在其他进程之间进行时间切换,因此您不能指望在大致相同的工作中完成相同数量的工作时间在不同的核心上。

    第一种方法更容易编码,并且会为您提供良好的并行性。