C#.net多线程

迪米塔尔·巴尔季耶夫(Dimitar Baldzhiev)

我正在尝试在名为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>作为输出对象。当然,如果数组令人满意,那么您可以分配resultsA而不是将其传递给List<T>构造函数以在最后创建一个全新的对象。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章