我正在实验如何在一个名为Grasshopper的包中使用C#.net优化一些数学操作(Rhino3D的一部分)。操作非常简单,但必须执行的列表很大,而且可能会变得更大。
我使用的是Parallel.ForEach和C#脚本中的列表,最终结果的数量低于预期。这很可能是因为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相同。
发布于 2015-04-17 22:12:15
你猜对了,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;
}注意:您的代码中也有另一个bug。您在所有任务中共享相同的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数组中的相同元素,而且数组本身也不会改变(即重新分配),因此这是完全安全的线程。
以上假设确实需要一个List<double>作为输出对象。当然,如果一个数组令人满意,那么您可以将results分配给A,而不是将它传递给List<T>构造函数,以便在最后创建一个全新的对象。
发布于 2015-04-17 22:30:28
一个更简单的解决方案可能是使用.AsParallel()并处理生成的ParallelEnumerable:
private void RunScript(double z, int x, List<double> y, ref object A)
{
A = y
.AsParallel().AsOrdered()
.Select(elem => Math.Pow((elem * x), z))
.ToList();
}发布于 2015-04-17 22:20:39
以下是另一种选择:
private void RunScript(double z, int x, List<double> y, ref object A) {
var temp = new System.Collections.Concurrent.BlockingCollection<double>();
System.Threading.Tasks.Parallel.ForEach(y, numb => {
double r = Math.Pow((numb * x), z);
temp.Add(r);
});
A = temp; // if needed you can A = temp.ToList();
}Peter很好地描述了代码中的问题,我认为他建议的第二个函数可能是最好的选择。尽管如此,看到替代方案并了解到.NET框架包含并发安全集合还是很好的。
https://stackoverflow.com/questions/29709857
复制相似问题