У меня есть CacheService, который хранит коллекцию в MemoryCache, а затем находит элемент из коллекции, который хранится в кэше. Учитывая многопоточность, я хочу убедиться, что только один работник может сохранить коллекцию в кеше и найти ее. Поэтому я использую lock
для синхронизации вызова и обеспечения безопасности потока.
public class MyCacheService
{
private readonly IMemoryCache _memoryCache = null;
static object myLock = new object();
public MyCacheService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
}
public async Task<Job> Find(int key, string title, int[] skills, Func<int, Task<List<Job>>> getJobs)
{
lock (myLock)
{
List<Job> cachedJobs = null;
if (!_memoryCache.TryGetValue(key, out cachedJobs))
{
// compilation error here 'cannot await in the body of a lock statement'
var jobs = await getJobs(key);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(30));
cachedJobs = _memoryCache.Set(key, cachedJobs, cacheEntryOptions);
}
if (cachedJobs != null)
{
var job = cachedJobs.Where(j => j.Title == title &&
!j.Skills.Except(skills).Any())
.FirstOrDefault();
if (job == null)
{
return null;
}
cachedJobs.Remove(job);
return job;
}
return null;
}
}
}
Делегат getJobs
является asyn c вызовом для получения заданий из базы данных. Так что я получаю ошибку cannot await in the body of a lock statement
Я понял, почему я получаю ошибку. Я могу использовать getJobs(key).GetAwaiter().GetResult()
для устранения ошибки
Существует LazyCache , который гарантирует однократную оценку делегата, результаты которого вы хотите кэшировать, и мы можем использовать asyn c делегат, но у меня нет использовал его
есть ли другие варианты?
ОБНОВЛЕНИЕ 1
Я попытался использовать SemaphoreSlim
, как предложено, однако это не работает, как ожидалось. В DEMO ниже у меня есть 10000 рабочих мест (5000 BASI C рабочих мест и 5000 рабочих мест мастера) и всего 200 рабочих. Первые 100 рабочих (1-100) предназначены для рабочих мест BASI C, а 101 - 200 рабочих - для мастеров.
Ожидается, что любой работник от 1 до 100 получит работу BASI C, а 101-200 - получить MASTER задание
SemaphoreSlim
не работает должным образом. При таком подходе все 5000 заданий BASI C всегда присваиваются работнику с идентификатором 1
. И все задания MASTER всегда назначаются для Worker с идентификатором 101
DEMO с использованием SemaphoreSlim
Мой первоначальный подход с использованием блокировки C#, кажется, работает, как и ожидалось, пока поскольку я не использую метод asyn c внутри блокировки
DEMO используя lock