【问题标题】:How to fill an array in multiple threads?如何在多个线程中填充数组?
【发布时间】:2021-03-22 22:51:20
【问题描述】:

有这么一个数组,我通过Thread知道需要什么,但是不明白怎么做。您是否需要将数组拆分为多个部分,或者您可以立即做某事吗?

Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();
                int[] a = new int[10000];
                Random rand = new Random();
    
                for (int i = 0; i < a.Length; i++)
                {
                    a[i] = rand.Next(-100, 100);
                }
                foreach (var p in a)
                    Console.WriteLine(p);
                TimeSpan ts = stopWatch.Elapsed;
                stopWatch.Stop();
                string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                ts.Hours, ts.Minutes, ts.Seconds,
                ts.Milliseconds / 10);
                Console.WriteLine("RunTime " + elapsedTime);

【问题讨论】:

  • 正如您从答案中了解到的那样,这是您通常不想做的事情,尽管这肯定是一个很好的教学练习。

标签: c# parallel-processing


【解决方案1】:

与 John Wu 相比,另一种方法是使用自定义分区器。我认为它更具可读性。

using System.Collections.Concurrent;
using System.Threading.Tasks;

int[] a = new int[10000];
int batchSize = 1000;
Random rand = new Random();

Parallel.ForEach(Partitioner.Create(0, a.Length, batchSize), range =>
{
     for (int i = range.Item1; i < range.Item2; i++)
     {
         a[i] = rand.Next(-100, 100);
     }
});

【讨论】:

  • 奇怪的是,多线程数组填充比填充 1 个线程需要更多时间。就算你填1,000,000,000
  • @ebw1910 这是因为处理器中的内存缓存。每次切换线程时,都会使缓存无效,因为另一个线程正在内存的不同区域上工作。这基本上抹去了缓存的好处。如果您使用一个线程按顺序填充整个数组,您就可以让处理器的内存访问优化按照设计的方式工作。
  • @ebw1910 您问的是如何在多个线程中用随机数填充数组,而不是如何更快地填充它。如果速度是您的目标,最好的办法是找到faster random number generator
  • 关于Parallel.ForEach 方法的使用,我的建议是始终通过parallelOptions 参数明确指定MaxDegreeOfParallelism。此属性的合理值是Environment.ProcessorCount。否则,Parallel.ForEach 方法将使用所有可用的 ThreadPool 线程,saturating the ThreadPool,直到源可枚举完成。
  • Rogn 是的,您的代码并行枚举了 10 个范围。因此,如果ThreadPool 已经可用(或可以按需立即创建)至少 10 个线程,则它不会饱和。否则会饱和。如果我在有 4 个内核的 PC 上运行您的代码,ThreadPool 很可能会在并行执行期间变得饱和。
【解决方案2】:

在现代 c# 中,您几乎永远不必使用 Thread 对象本身——它们充满危险,并且还有其他语言功能也可以完成这项工作(参见 asyncTPL )。我将向您展示一种使用 TPL 的方法。

注意:由于false sharing 的问题,您需要进行装配,以便不同的线程在不同的内存区域上工作。否则,您将看不到性能上的任何提升——实际上,性能可能会变得相当糟糕。在这个例子中,我将数组分成每个 4,000 字节(1,000 个元素)的块,并在单独的线程中处理每个块。

using System.Threading.Tasks;

var array = new int[10000];
var offsets = Enumerable.Range(0, 10).Select( x => x * 1000 );
Parallel.ForEach( offsets, offset => {
    for ( int i=0; i<1000; i++ )
    {
        array[offset + i] = random.Next( -100,100 );
    }
});

话虽如此,我怀疑您是否会在此示例中看到很多性能提升 - 数组太小,不值得额外开销。

【讨论】:

  • 非常感谢!
  • @ebw1910 当然是。您有多个线程试图同时使用相同的资源。结果是资源争用。
  • 提醒:Randomis not thread-safe!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
  • 2016-06-03
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
相关资源
最近更新 更多