简体   繁体   English

C#多个任务不能并行运行

[英]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.). 我试图创建一个多线程骰子滚动模拟-只是出于好奇心,多线程编程的乐趣,并向其他人展示“随机结果”的效果(许多人不明白,如果您连续六次滚动拉普拉斯骰子,您已经有1、2、3、4、5,则下一卷不是6.)。 To show them the distribution of n rolls with m dice I created this code. 为了向他们展示n个骰子与m个骰子的分布,我创建了此代码。

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. 随着完成时间的快速增长,多线程模拟6个或更多骰子的“成千上万”的重新滚动是合理的。

I read several examples from msdn that all indicate that there should be several tasks running simultanously. 我从msdn阅读了几个示例,所有示例都表明应该同时运行多个任务。 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) (甚至,当我尝试一次运行400个骰子并重新滚动1000万次时)

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. 第一维:每个骰子1个条目,第二维是每个骰子滚动的眼睛分布。

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. 接下来,我创建一个任务数组,每个任务返回一个结果数组(如上所述,第二个维度)。每个数组都有6个条目,分别代表拉普拉斯W6骰子的每一面。 If the dice roll results in 1 eye the first entry [0] is increased by +1. 如果骰子掷出一只眼睛,则第一项[0]增加+1。 So you can visualize how often each value has been rolled. 因此,您可以直观地看到每个值的滚动频率。

Then I use a plain for-loop to start all threads. 然后,我使用一个普通的for循环启动所有线程。 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); Task.WaitAll(任务); to Task.WhenAll(tasks); 到Task.WhenAll(tasks);

Again my quation: Why doesn't that code utilize more than one core of my CPU? 再说一次我的话:为什么这些代码不占用我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 . 这里的问题是tasks[ctr].Result The .Result portion itself waits for the function to complete before storing the resulting int array into StoreResult. .Result部分本身会等待函数完成,然后再将结果int数组存储到StoreResult中。 Instead, make a new loop after Task.WaitAll to get your results. 而是在Task.WaitAll之后创建一个新循环以获取结果。

You may consider doing a Parallel.Foreach loop instead of manually creating separate tasks for this. 您可以考虑执行Parallel.Foreach循环,而不是为此手动创建单独的任务。

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). 非常重要的注意事项: C#随机数生成器不是线程安全的 (有关此主题的讨论,另请参见此MSDN博客文章 )。 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. 如果不确保以线程安全的方式访问Random对象,则对返回随机数的方法的调用将返回0。

Also, just to be nit-picky, using a Task is not really the same thing as doing multithreading; 而且,只是挑剔,使用Task与进行多线程并不是一回事。 while you are, in fact, doing multithreading here, it's also possible to do purely asynchronous, non-multithreaded code with async/await. 实际上,尽管您在这里进行多线程处理,但也可以使用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 / O绑定操作,在该操作中创建一个单独的线程只是为了等待结果是没有意义的(但最好是允许调用线程在等待结果的同时做其他工作)。

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). 如果我正确阅读此文件,则该状态可变状态(但不是共享的可变状态)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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