【问题标题】:Thread safety Parallel.For c#线程安全Parallel.For c#
【发布时间】:2015-08-11 23:24:57
【问题描述】:

我是法语,很抱歉首先对不起我的英语。

我在 Visual Studio 上遇到错误(索引超出范围)我只在 Parallel.For 上遇到这个问题。在经典 for 上没有。

我认为一个线程想要访问我的数组 [i] 而另一个线程也想要 ..

这是一个计算 Kmeans 聚类的代码,用于在文档之间建立链接(具有余弦相似度)。

更多信息:

  • IndexOutOfRange 在similarityMeasure[i]=.....

  • 我有一台带有 2 个处理器(12 个逻辑)的计算机

  • 使用经典 for ,cpu 使用率为 9-14% ,1 次迭代的时间=9min..

  • 使用 parallel.for,cpu 使用率为 70-90% =p,1 次迭代的时间 =~1min30

  • 有时它会在产生错误之前工作更长时间

我的功能是:

    private static int FindClosestClusterCenter(List<Centroid> clustercenter, DocumentVector obj)
{
    float[] similarityMeasure = new float[clustercenter.Count()];
    float[] copy = similarityMeasure;
    object sync = new Object();

  Parallel.For(0, clustercenter.Count(), (i) =>      //for(int i = 0; i < clustercenter.Count(); i++)  Parallel.For(0, clustercenter.Count(), (i) =>  //
       {
                similarityMeasure[i] = SimilarityMatrics.FindCosineSimilarity(clustercenter[i].GroupedDocument[0].VectorSpace, obj.VectorSpace);

       });

    int index = 0;
    float maxValue = similarityMeasure[0];
    for (int i = 0; i < similarityMeasure.Count(); i++)
    {
        if (similarityMeasure[i] > maxValue)
        {
            maxValue = similarityMeasure[i];
            index = i;
        }

    }
    return index;
}

我的函数在这里调用:

do
            {
                prevClusterCenter = centroidCollection;
                DateTime starttime = DateTime.Now;

                  foreach (DocumentVector obj in documentCollection)//Parallel.ForEach(documentCollection, parallelOptions, obj =>//foreach (DocumentVector obj in documentCollection)
                   {

                       int ind = FindClosestClusterCenter(centroidCollection, obj);

                       resultSet[ind].GroupedDocument.Add(obj);

                   }
                TimeSpan tempsecoule = DateTime.Now.Subtract(starttime);
                Console.WriteLine(tempsecoule);
                //Console.ReadKey();
                InitializeClusterCentroid(out centroidCollection, centroidCollection.Count());
                centroidCollection = CalculMeanPoints(resultSet);
                stoppingCriteria = CheckStoppingCriteria(prevClusterCenter, centroidCollection);
                if (!stoppingCriteria)
                {
                    //initialisation du resultat pour la prochaine itération
                    InitializeClusterCentroid(out resultSet, centroidCollection.Count);
                }
            } while (stoppingCriteria == false);
            _counter = counter;
            return resultSet;

查找CosSimilarity:

 public static float FindCosineSimilarity(float[] vecA, float[] vecB)
        {
            var dotProduct = DotProduct(vecA, vecB);
            var magnitudeOfA = Magnitude(vecA);
            var magnitudeOfB = Magnitude(vecB);
            float result = dotProduct / (float)Math.Pow((magnitudeOfA * magnitudeOfB),2);
            //when 0 is divided by 0 it shows result NaN so return 0 in such case.
            if (float.IsNaN(result))
                return 0;
            else
                return (float)result;

        }

计算均值点:

 private static List<Centroid> CalculMeanPoints(List<Centroid> _clust)
        {
            for (int i = 0; i < _clust.Count(); i++)
            {
                if (_clust[i].GroupedDocument.Count() > 0)
                {
                    for (int j = 0; j < _clust[i].GroupedDocument[0].VectorSpace.Count(); j++)
                    {
                        float total = 0;
                        foreach (DocumentVector vspace in _clust[i].GroupedDocument)
                        {
                            total += vspace.VectorSpace[j];
                        }

                        _clust[i].GroupedDocument[0].VectorSpace[j] = total / _clust[i].GroupedDocument.Count();
                    }
                }
            }
            return _clust;
        }

【问题讨论】:

  • 哪一行有错误?是similarityMeasure[i] = ...
  • 是的 "IndexOutOfRange 是在similarityMeasure[i]=....." :)
  • 如果您在 lambda 中使用实际的函数调用而不是匿名方法,那么您应该能够看到 i 中断时的值。这可以帮助您了解有关问题的更多信息。
  • 不要在Parallel.For 中使用lock - 按照您的处理方式,Parallel.For 的主体永远不能并行执行。您显示的代码没有您报告的问题。请检查该代码是否确实是造成您麻烦的代码。哦,clustercenter.Count() 显然会随着时间而变化 - 如果你想坚持你的算法(这似乎不正确),至少将值存储在局部变量中。
  • @Keithin8a 您可以在匿名方法中轻松查看i 的值。至少在 Visual Studio 中是这样的:)

标签: c# multithreading for-loop parallel-processing


【解决方案1】:

FindCosineSimilarity可能会有一些副作用,请确保它没有修改任何字段或输入参数。示例:resultSet[ind].GroupedDocument.Add(obj);。如果resultSet 不是对本地实例化数组的引用,那么它就是side effect。

这可能会解决它。但仅供参考,您可以为此使用AsParallel 而不是Parallel.For:

similarityMeasure = clustercenter
      .AsParallel().AsOrdered()
      .Select(c=> SimilarityMatrics.FindCosineSimilarity(c.GroupedDocument[0].VectorSpace, obj.VectorSpace))
      .ToArray();

【讨论】:

  • 即使resultSet 是本地的,它也很可能是对其他字段的引用(例如,稍后使用的clustercenter[i])。或者resultSet 中的GroupedDocument 可能与clustercenter[i] 中的引用相同。在处理引用类型时,“本地”并没有完全削减它:/
  • @Luaan 好点,已更改为“对本地实例化数组的引用”
  • Ty,但我在 .AsArray() 'System.Linq.Parallel.Query 上出现错误,没有 AsArray 的定义
  • @Lilianurvoy 对不起,我是想写ToArray
  • @Weston 我用 ToArray() 进行了测试,第一次它可以工作(更快)但第二次,同样的错误
【解决方案2】:

您意识到,如果您同步 Parallel-For 的全部内容,这与正常的同步 for 循环一样,对吧?这意味着代码不会并行执行任何操作,因此我认为您不会遇到任何并发问题。据我所知,我的猜测是 clustercenter[i].GroupedDocument 可能是一个空数组。

【讨论】:

  • 是的,很明显。但是,CPU 使用率如何达到 90% 的并行,以及它如何更快地完成?我认为 OP 发布的代码很可能不是显示问题的实际代码,但也许在 sync 变量或其他东西上有一些奇怪的关闭(但为什么?)? :D
  • Ty 为您的帖子。例如,我想要 45 个 clustercenter 和 clustercenter.count =45 ... 所以?
  • 同步变量仅用于测试导致我的错误...这里不需要
  • 对不起,我不明白你想说什么@Lilian。也许您可以添加对您尝试访问代码的所有数组大小的跟踪?或者只是在访问之前测试该字段? (如果(clustercenter[i].GroupedDocument.Length == 0) doSomething(); - 你明白我的意思)@Luaan 不相信你没有伪造自己的统计数据 ;-) 不,除了笑话我不知道那里发生了什么我怀疑那里的这段代码是并行运行的。跨度>
  • 我用经典的 For 进行了测试,我有同样的错误...... =O 6 次迭代后对象编号“1”(我的列表中的第一个对象)的索引问题这次 i=33 GroupedDocument[ 32]=100 分组文档[33]=0 分组文档[34]=5
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多