我尝试创建一个多线程骰子滚动模拟 - 只是为了好奇心,多线程编程的乐趣,并向其他人展示"随机结果" (很多人都不明白,如果你连续六次掷骰子,你已经有1,2,3,4,5个下一卷不是6)。为了向他们展示m个骰子的n个卷的分布,我创建了这个代码。
好吧,结果很好,但即使我为每个骰子创建一个新任务,程序运行单线程。 多线程可以合理地模拟数百万" 6个或更多骰子的重新卷入,因为完成时间将迅速增长。
我从msdn中读到了几个例子,这些例子都表明应该有几个任务同时运行。 有人可以给我一个提示,为什么这段代码不使用很多线程/核心? (当我尝试一次运行400个骰子和1000万次重新注册时,甚至没有)
首先,我初始化存储结果的锯齿状数组。第一维度:每个骰子1个进入,第二个维度是每个骰子滚动的眼睛的分布。
接下来,我创建一个任务数组,每个任务返回一个结果数组(第二个维度,如上所述) 这些阵列中的每一个都有6个条目,代表拉普拉斯W6骰子的每一侧。如果骰子滚动导致1只眼睛,则第一个条目[0]增加+1。因此,您可以看到每个值的滚动频率。
然后我使用一个普通的for循环来启动所有线程。在所有线程都启动之前,没有迹象表明等待线程。
最后我等待所有人完成并总结结果。如果改变
,它没有任何区别Task.WaitAll(任务);至 Task.WhenAll(任务);
再次我的观点:为什么该代码不使用我的CPU的多个核心?我需要改变什么?
提前致谢!
以下是代码:
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
}
}
答案 0 :(得分:2)
此处的问题是tasks[ctr].Result
。在将结果int数组存储到StoreResult之前,.Result
部分本身等待函数完成。相反,请在Task.WaitAll
之后创建一个新循环以获得结果。
答案 1 :(得分:1)
您可以考虑执行Parallel.Foreach循环,而不是为此手动创建单独的任务。
正如其他人所指出的那样,当你尝试聚合时,你最终会等待每个单独的任务完成,所以这实际上并不是多线程的。
非常重要的说明: C#随机数生成器is not thread safe(有关该主题的讨论,另请参阅此MSDN blog post)。不要在多个线程之间共享相同的实例。来自documentation:
...随机对象不是线程安全的。如果您的应用调用随机 从多个线程的方法,您必须使用同步对象 确保只有一个线程可以访问随机数生成器 一次。如果您不确保在a中访问Random对象 线程安全的方式,对返回随机数的方法的调用返回0。
另外,只是为了挑剔,使用Task
与多线程并不是一回事;实际上,在这里进行多线程处理时,也可以使用async / await执行纯异步,非多线程代码。这主要用于I / O绑定操作,其中创建一个单独的线程以等待结果毫无意义(但是在等待结果时允许调用线程做其他工作)。
我认为在分配给主阵列时你不必担心线程安全(假设每个线程只分配给数组中的特定索引而没有其他人分配给同一个内存位置) ;当多个线程同时访问/修改共享可变状态时,您只需担心锁定。如果我正确读取此内容,则此是可变状态(但它不是共享可变状态)。