我正在尝试在名为Grasshopper(Rhino3D的一部分)的程序包中使用C#.net优化某些数学运算。该操作非常简单,但是必须执行的操作列表很大,并且可能会更大。
我在C#脚本中使用Parallel.ForEach和列表,得到的最终结果数比预期的要少。这很可能是由于list.add不是线程安全的(或我在其之上构建的软件中的线程安全的)这一事实。
private void RunScript(double z, int x, List<double> y, ref object A)
{
List<double> temp = new List<double>();
double r;
System.Threading.Tasks.Parallel.ForEach(y, numb =>
{
r = Math.Pow((numb * x), z);
temp.Add(r);
});
A = temp;
请帮助我找出使用CPU多线程对数百个值运行此简单数学运算的简单有效方式(或者如果您对GPU CUDA有建议)。
我希望晦涩难懂的特定软件不会打扰您,因为据我所知,它的性能与普通C#.Net / Python / VB.Net相同。
您正确地猜测,List<T>
不是线程安全的。您必须同步对其任何实例的访问。
一种选择是简单地在每个任务中同步:
private void RunScript(double z, int x, List<double> y, ref object A)
{
List<double> temp = new List<double>();
object l = new object();
System.Threading.Tasks.Parallel.ForEach(y, numb =>
{
double r = Math.Pow((numb * x), z);
lock (l) temp.Add(r);
});
A = temp;
}
注意:您的代码中也存在另一个错误。您r
在所有任务中共享相同的变量,这可能导致同一值被两次或多次添加到结果中,而其他值则被忽略。我通过简单地将变量声明移至用于ForEach()
调用的匿名方法的主体来修复了该错误。
另一个选择是认识到您预先知道将有多少个结果,因此可以简单地初始化一个足够大以包含所有结果的数组:
private void RunScript(double z, int x, List<double> y, ref object A)
{
double[] results = new double[y.Count];
System.Threading.Tasks.Parallel.For(0, y.Count, i =>
{
// read-only access of `y` is thread-safe:
results[i] = Math.Pow((y[i] * x), z);
});
A = new List<double>(results);
}
没有两个线程会尝试访问results
数组中的同一元素,并且数组本身也永远不会更改(即重新分配),因此这是线程安全的。
以上假设您确实确实需要aList<double>
作为输出对象。当然,如果数组令人满意,那么您可以分配results
给A
而不是将其传递给List<T>
构造函数以在最后创建一个全新的对象。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句