ConcurrentBag 和应用程序因 .NET 中的 LINQ 检索而挂起
ConcurrentBag and application hang with LINQ retrieval in .NET
为了线程安全,我从常规列表切换到 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>
方法的documentation:
All public and protected members of ConcurrentBag<T>
are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentBag<T>
implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.
FirstOrDefault
LINQ 运算符是 IEnumerable<T>
的扩展,是 ConcurrentBag<T>
实现的接口,因此您正式处于“未定义行为”领域。也就是说,并且知道 FirstOrDefault
是如何实现的,我认为它没有理由导致您的应用程序挂起。
关于 ConcurrentBag<T>
是否适合您正在做的事情这个问题,我认为这不太可能。 ConcurrentBag<T>
是一个very specialized collection, making it a bad option for anything that it's not a mixed producer-consumer scenario. And judging from the code that you posted, your scenario is not one of those. I would suggest to switch to a ConcurrentQueue<T>
,它是一个支持并发入队(添加)和枚举的集合,并且还保留了它包含的项的插入顺序。
为了线程安全,我从常规列表切换到 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>
方法的documentation:
All public and protected members of
ConcurrentBag<T>
are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces theConcurrentBag<T>
implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.
FirstOrDefault
LINQ 运算符是 IEnumerable<T>
的扩展,是 ConcurrentBag<T>
实现的接口,因此您正式处于“未定义行为”领域。也就是说,并且知道 FirstOrDefault
是如何实现的,我认为它没有理由导致您的应用程序挂起。
关于 ConcurrentBag<T>
是否适合您正在做的事情这个问题,我认为这不太可能。 ConcurrentBag<T>
是一个very specialized collection, making it a bad option for anything that it's not a mixed producer-consumer scenario. And judging from the code that you posted, your scenario is not one of those. I would suggest to switch to a ConcurrentQueue<T>
,它是一个支持并发入队(添加)和枚举的集合,并且还保留了它包含的项的插入顺序。