1

There is such an array, I know what is needed through Thread, but I don’t understand how to do it. Do you need to split the array into parts, or can you do something right away?

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);
ebw1910
  • 75
  • 6
  • 1
    As you've learned from the answers, this is something you generally don't want to do, though it's certainly a good pedagogical exercise. – phoog Mar 23 '21 at 00:21

2 Answers2

3

Another approach, compared to John Wu's, is to use a custom partitioner. I think that it is a little more readable.

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);
     }
});
Rogn
  • 969
  • 8
  • 5
  • The oddity is that when multithreaded array filling takes more time than when filling in 1 thread. Even if you fill in 1,000,000,000 – ebw1910 Mar 22 '21 at 23:46
  • 1
    @ebw1910 this is because of memory caching in the processor. Each time you switch threads you invalidate the cache, because the other thread is working on a different area of the memory. This basically erases the benefit of the cache. If you fill the whole array sequentially with one thread, you allow the processor's memory access optimizations to work as they were designed to work. – phoog Mar 23 '21 at 00:19
  • 2
    @ebw1910 you asked how to fill an array with random numbers in multiple threads, not how to fill it faster. If speed is your goal, your best bet is to find a [faster random number generator](https://stackoverflow.com/questions/1790776/fast-random-generator). – Theodor Zoulias Mar 23 '21 at 00:43
  • 1
    Regarding the usage of the `Parallel.ForEach` method, my recommendation is to always specify explicitly the `MaxDegreeOfParallelism`, through the `parallelOptions` parameter. A reasonable value for this property is `Environment.ProcessorCount`. Otherwise the `Parallel.ForEach` method uses all the available `ThreadPool` threads, [saturating the `ThreadPool`](https://stackoverflow.com/questions/66261010/multiple-parallel-foreach-loops-in-net/66263583#66263583) until the source enumerable completes. – Theodor Zoulias Mar 23 '21 at 00:52
  • @TheodorZoulias That is not quite correct. The max number of threads used in my example will be 10000/1000 = 10. – Rogn Mar 23 '21 at 10:47
  • 1
    Rogn yes, your code enumerates 10 ranges in parallel. So if the `ThreadPool` has already available (or can create instantly on demand) at least 10 threads, it will not be saturated. Otherwise it will be saturated. If I run your code in my PC, that has 4 cores, the `ThreadPool` will most probably become saturated during the parallel execution. – Theodor Zoulias Mar 23 '21 at 12:11
2

In modern c#, you should almost never have to use Thread objects themselves-- they are fraught with peril, and there are other language features that will do the job just as well (see async and TPL). I'll show you a way to do it with TPL.

Note: Due to the problem of false sharing, you need to rig things so that the different threads are working on different memory areas. Otherwise you will see no gain in performance-- indeed, performance could get considerably worse. In this example I divide the array into blocks of 4,000 bytes (1,000 elements) each and work on each block in a separate thread.

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 );
    }
});

That all being said, I doubt you'll see much of a gain in performance in this example-- the array is much too small to be worth the additional overhead.

John Wu
  • 50,556
  • 8
  • 44
  • 80