ConcurrentBag 和应用程序在 .NET 中与 LINQ 检索挂起



为了线程安全,我从常规列表切换到ConcurrentBag,因为该列表引起了一些冲突。 该列表存储在缓存中,如果找到新项目,则会将其添加到具有对缓存列表的引用的列表中。 我最近挂在下面的FirstOrDefault(常规 LINQ,而不是数据库)代码上。 我不确定为什么它会挂在这里。 这是一个高性能网站,此列表经常被点击,存储在缓存中并使用ConcurrentBag对于我正在做的事情来说是一个不错的选择吗?

呼叫代码

var mobileApplicationsCached = GetMobileApplicationsCached();
var mobileApplicationCache = new AppDownloadModel();
if (mobileApplicationsCached != null)
{
mobileApplicationCache = mobileApplicationsCached
.FirstOrDefault(t => t != null && t.EventId == eventId ||
(t.EventIds != null && t.EventIds.Contains(eventId)));

获取缓存列表

public ConcurrentBag<AppDownloadModel> GetMobileApplicationsCached()
{
return CacheHelper.GetInitializedCache(Config.Cache.Keys.MobileApplications, () =>
{
return new CacheData<ConcurrentBag<AppDownloadModel>>
{
Data = new ConcurrentBag<AppDownloadModel>()
};
}, Config.Cache.FourHours, false);
}

转储分析

000000a3`24c096d0 00007ffc`37508b02     : 0000008f`03cb2ec8 00007ffc`8d3709f5 000000a3`24c09710 0000008f`8b566f98 : mscorlib!System.Collections.Generic.List<int>.Contains+0x38
000000a3`24c09720 00007ffc`3248e17a     : 0000008f`8b566e50 0000008f`03cb2ec8 00000000`00000000 00000000`00000000 : Tournaments_Services!Tournaments.Services.Mobile.MobileApplicationsService.<>c__DisplayClass5_0.<GetEventMobileApplication>b__0+0x82
000000a3`24c09770 00007ffc`374e42ed     : 0000008f`8b566ed0 000000a3`24c09870 00007ffc`8e53bd50 00007ffc`8e53e78f : System_Core!System.Linq.Enumerable.FirstOrDefault<Tournaments.Models.App.AppDownloadModel>+0xba
000000a3`24c097e0 00007ffc`3757902c     : 0000008f`8b55f2d8 00000000`0002a2ac 00000000`00006a00 00000000`00000000 : Tournaments_Services!Tournaments.Services.Mobile.MobileApplicationsService.GetEventMobileApplication+0x13d
000000a3`24c0a0d0 00007ffc`3795fd79     : 0000008f`8b561b88 00000000`0002a2ac 00006a00`24c0a301 0000008f`00000000 : Tournaments!Tournaments.Controllers.BaseEventController.AppleApplicationId+0xac

根据ConcurrentBag<T>方法的文档:

ConcurrentBag<T>的所有公共成员和受保护成员都是线程安全的,可以从多个线程并发使用。但是,通过ConcurrentBag<T>实现的接口之一(包括扩展方法)访问的成员不能保证线程安全,可能需要由调用方同步。

FirstOrDefaultLINQ运算符是IEnumerable<T>上的扩展,ConcurrentBag<T>实现的接口,因此正式您处于"未定义行为"领域。也就是说,并且知道FirstOrDefault是如何实现的,我认为它没有理由导致您的应用程序挂起。

关于ConcurrentBag<T>是否适合您正在做的事情的问题,我想说这不太可能。ConcurrentBag<T>是一个非常专业的集合,对于任何不是生产者和消费者混合场景的人来说,它都是一个糟糕的选择。从您发布的代码来看,您的方案不是其中之一。我建议切换到ConcurrentQueue<T>,这是一个支持同时排队(添加)和枚举的集合,并且还保留了它所包含项目的插入顺序。

最新更新