ConcurrentBag <t>获取重复项(似乎不是线程安全的)</t>

时间:2014-02-07 12:02:35

标签: c# parallel-extensions

我必须在某处做错事,因为我在我的concurrentbag中得到了重复的项目,这里是事件链

  var listings = new ConcurrentBag<JSonListing>();
  Parallel.ForEach(Types, ParallelOptions, (type, state) =>
  {
      ...
      status = ProcessType(listings, status, type, state);
      ....
   });

  private GeocodeResults ProcessType(ConcurrentBag<JSonListing> listings, GeocodeResults status, XElement type, ParallelLoopState state)
  {
      ....
      AddListingsToList(results, type, listings);
      .... 
  }

private void AddListingsToList(dynamic results, XElement type, ConcurrentBag<JSonListing> listings)
    {

        var typeMustMatch = type.Attribute("TypeMustMatch").Value;
        var typeID = Convert.ToInt32(type.Attribute("ID").Value);

        foreach (var result in results.results)
        {


            var foundListing = listings.SingleOrDefault(x => x.ID == result.id);
            if (foundListing != null)
            {
                var typeIDs = foundListing.TypeIDs.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
                if (!typeIDs.Contains(typeID.ToString()))
                {
                    foundListing.TypeIDs += "|" + typeID;
                }
            }
            else
            {
                var listing = new JSonListing { ID = result.id, ReferenceNumber = result.reference, TypeIDs = typeID.ToString(), TypeMustMatch = typeMustMatch };
                listings.Add(listing);
            }




        }


    }

添加列表应该保证如果元素存在,则不要添加另一个相同的ID,而是更新一些属性。 现在我得到的错误是

  

System.InvalidOperationException:Sequence包含多个匹配元素
     在System.Linq.Enumerable.SingleOrDefault [TSource](IEnumerable`1 source,Func`2谓词)
     at LocalSearch.Processor.CityProcessor.AddListingsToList(Object results,XElement type,ConcurrentBag`1 listing)在d:\ Projects \ ListingLocator2 \ Code \ LocalSearch.Processor \ Processors.cs:第310行
     在CallSite.Target(Closure,CallSite,CityProcessor,Object,XElement,ConcurrentBag`1)
     在LocalSearch.Processor.CrocessProcessor.ProcessType(ConcurrentBag`1列表,GeocodeResults状态,XElement类型,ParallelLoopState状态)中的d:\ Projects \ ListingLocator2 \ Code \ LocalSearch.Processor \ Processors.cs:第249行      在LocalSearch.Processor.CityProcessor。&lt;&gt; c__DisplayClass4.b__0(XElement类型,ParallelLoopState状态)在d:\ Projects \ ListingLocator2 \ Code \ LocalSearch.Processor \ Processors.cs:第137行

2 个答案:

答案 0 :(得分:18)

ConcurrentBag保证其上的每个操作在单独考虑时都是线程安全的。它保证连续的多个操作将被视为原子组。

因此,您的代码具有竞争条件:您检查行李是否已包含某个项目X,但是两个线程可以同时运行测试,确定该项目不存在,然后继续添加它。最终结果:该物品的两个副本最终放入包中。

通过使用ConcurrentDictionary代替并利用{em> 原子的TryAdd方法,看起来您的用例会更好。或者你可以在包里放一个lock()来使块内的所有内容都以原子方式运行,但是你不需要并发收集,而是可以使用直接List

答案 1 :(得分:8)

这并不意味着Concurrent Bag不是线程安全的,它只是意味着你的代码不是

您正在检查并发行李中的值,然后在前一次检查失败时添加新项目。

但是,由于代码中没有锁,因此两个作业可以同时执行以下操作;

THREAD 1        THREAD 2
=-=-=-=-=-=-=-=-=-=-=-=-
Check Exists
                Check Exists
Add New
                Add New

您需要锁定支票并添加例程。