首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#.net多线程

C#.net多线程
EN

Stack Overflow用户
提问于 2015-04-17 21:41:54
回答 5查看 1.5K关注 0票数 15

我正在实验如何在一个名为Grasshopper的包中使用C#.net优化一些数学操作(Rhino3D的一部分)。操作非常简单,但必须执行的列表很大,而且可能会变得更大。

我使用的是Parallel.ForEach和C#脚本中的列表,最终结果的数量低于预期。这很可能是因为list.add并不是线程安全的(或者在我正在构建的软件中也不是线程安全的)。

代码语言:javascript
复制
  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相同。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-04-17 22:12:15

你猜对了,List<T>并不是线程安全的.必须同步对其任何实例的访问。

一种选择是简单地在每个任务中同步:

代码语言:javascript
复制
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()调用的匿名方法的主体,我修复了这个错误。

另一种选择是认识到预先知道将有多少结果,因此可以简单地初始化一个足够大的数组,以包含所有结果:

代码语言:javascript
复制
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>构造函数,以便在最后创建一个全新的对象。

票数 15
EN

Stack Overflow用户

发布于 2015-04-17 22:30:28

一个更简单的解决方案可能是使用.AsParallel()并处理生成的ParallelEnumerable

代码语言:javascript
复制
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();
}
票数 7
EN

Stack Overflow用户

发布于 2015-04-17 22:20:39

以下是另一种选择:

代码语言:javascript
复制
    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框架包含并发安全集合还是很好的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29709857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档