I switched from a regular list to a ConcurrentBag
for thread safety because the list was causing some clashes. The list is stored in cache and updated if a new item is found it is added to the list which has a reference to the cache list. I recently had a hang at the FirstOrDefault
(regular LINQ, not database) code below on it. I am not sure why it would hang here. This is a high performance site and this list is hit regularly, is storing in the cache and using ConcurrentBag
a good option for what I am doing?
Calling Code
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)));
Get Cache List
public ConcurrentBag<AppDownloadModel> GetMobileApplicationsCached()
{
return CacheHelper.GetInitializedCache(Config.Cache.Keys.MobileApplications, () =>
{
return new CacheData<ConcurrentBag<AppDownloadModel>>
{
Data = new ConcurrentBag<AppDownloadModel>()
};
}, Config.Cache.FourHours, false);
}
Dump Analysis
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
CodePudding user response:
According to the documentation of the ConcurrentBag<T>
method:
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.
The FirstOrDefault
LINQ operator is an extension on the IEnumerable<T>
, an interface that the ConcurrentBag<T>
implements, so officially you are in the "undefined behavior" territory. That said, and knowing how the FirstOrDefault
is implemented, I see no reason for it to cause your application to hang.
Regarding the question whether a ConcurrentBag<T>
is a good option for what you are doing, I would say it's unlikely. The ConcurrentBag<T>
is a 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>
, which is a collection that supports enqueuing (adding) and enumerating concurrently, and also preserves the insertion order of the items it contains.