简体   繁体   中英

c# multiple tasks do not run in parallel

I tried to create a multithreaded dice rolling simulation - just for curiosity, the joy of multithreaded progarmming and to show others the effects of "random results" (many people can't understand that if you roll a laplace dice six times in a row and you already had 1, 2, 3, 4, 5 that the next roll is NOT a 6.). To show them the distribution of n rolls with m dice I created this code.

Well, the result is fine BUT even though I create a new task for each dice the program runs single threaded. Multithreaded would be reasonable to simulate "millions" of rerolls with 6 or more dice as the time to finish will grow rapidly.

I read several examples from msdn that all indicate that there should be several tasks running simultanously. Can someone please give me a hint, why this code does not utilize many threads / cores? (Not even, when I try to run it for 400 dice at once and 10 Million rerolls)

At first I initialize the jagged Array that stores the results. 1st dimension: 1 Entry per dice, the second dimension will be the distribution of eyes rolled with each dice.

Next I create an array of tasks that each return an array of results (the second dimension, as described above) Each of these arrays has 6 entries that represent eachs side of a laplace W6 dice. If the dice roll results in 1 eye the first entry [0] is increased by +1. So you can visualize how often each value has been rolled.

Then I use a plain for-loop to start all threads. There is no indication to wait for a thread until all are started.

At the end I wait for all to finish and sum up the results. It does not make any difference if change

Task.WaitAll(tasks); to Task.WhenAll(tasks);

Again my quation: Why doesn't that code utilize more than one core of my CPU? What do I have to change?

Thanks in advance!

Here's the code:

private void buttonStart_Click(object sender, RoutedEventArgs e)
    {      
        int tries = 1000000;
        int numberofdice = 20   ;
        int numberofsides = 6; // W6 = 6
        var rnd = new Random();

        int[][] StoreResult = new int[numberofdice][];
        for (int i = 0; i < numberofdice; i++)
        {
            StoreResult[i] = new int[numberofsides];
        }

        Task<int[]>[] tasks = new Task<int[]>[numberofdice];


        for (int ctr = 0; ctr < numberofdice; ctr++)
        {

            tasks[ctr] = Task.Run(() =>
            {
                int newValue = 0;
                int[] StoreTemp = new int[numberofsides]; // Array that represents how often each value appeared
                for (int i = 1; i <= tries; i++) // how often to roll the dice
                {
                    newValue = rnd.Next(1, numberofsides + 1); // Roll the dice; UpperLimit for random int EXCLUDED from range 
                    StoreTemp[newValue-1] = StoreTemp[newValue-1] + 1; //increases value corresponding to eyes on dice by +1
                }
                return StoreTemp;
            });
                StoreResult[ctr] = tasks[ctr].Result; // Summing up the individual results for each dice in an array

        }
        Task.WaitAll(tasks);

          // do something to visualize the results - not important for the question

        }
    }

The issue here is tasks[ctr].Result . The .Result portion itself waits for the function to complete before storing the resulting int array into StoreResult. Instead, make a new loop after Task.WaitAll to get your results.

You may consider doing a Parallel.Foreach loop instead of manually creating separate tasks for this.

As others have indicated, when you try to aggregate this you just end up waiting for each individual task to finish, so this isn't actually multi-threaded.

Very important note: The C# random number generator is not thread safe (see also this MSDN blog post for discussion on the topic). Don't share the same instance between multiple threads. From the documentation :

...Random objects are not thread safe. If your app calls Random methods from multiple threads, you must use a synchronization object to ensure that only one thread can access the random number generator at a time. If you don't ensure that the Random object is accessed in a thread-safe way, calls to methods that return random numbers return 0.

Also, just to be nit-picky, using a Task is not really the same thing as doing multithreading; while you are, in fact, doing multithreading here, it's also possible to do purely asynchronous, non-multithreaded code with async/await. This is used mostly for I/O-bound operations where it's largely pointless to create a separate thread just to wait for a result (but it's desirable to allow the calling thread to do other work while it's waiting for the result).

I don't think you should have to worry about thread safety while assigning to the main array (assuming that each thread is assigning only to a specific index in the array and that no one else is assigning to the same memory location); you only have to worry about locking when multiple threads are accessing/modifying shared mutable state at the same time. If I'm reading this correctly, this is mutable state (but it's not shared mutable state).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM