using System.Threading.Tasks;
using System.Collections.Generic;
using System.Diagnostics;
public static async Task Main()
var svc = new Services.Service();
var tasks = new Task[threadCount];
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < threadCount; i++)
tasks[i] = Task.Run(() =>
Console.WriteLine($"Preço do produto #5 R$ {svc.GetPrice(5)}");
await Task.WhenAll(tasks);
Console.WriteLine($"Todas as tarefas foram executadas em {stopwatch.ElapsedMilliseconds} ms.");
private Infrastructure.SafeCacheService SafeCacheService = new();
private Infrastructure.Data.ProductRepository Repository = new();
public decimal? GetPrice(int productId, bool useCache = true)
? SafeCacheService.GetFromCacheOrExecute<decimal?>($"cache:product:{productId}:price", TimeSpan.FromSeconds(2), 5, () => this.Repository.GetPriceByProduct(productId))
: this.Repository.GetPriceByProduct(productId);
public class SafeCacheService
private Infrastructure.PseudoRedis Redis = new();
public T? GetFromCacheOrExecute<T>(string key, TimeSpan lockTimeOut, int retry, Func<T?> longWayFunc)
T? returnValue = this.Redis.Get<T?>(key);
string lockKey = $"{key}:lock";
using (var lockcontrol = this.Redis.Lock(key: lockKey, timeout: TimeSpan.FromSeconds(2), retry: 5))
returnValue = this.Redis.Get<T?>(key);
Console.WriteLine($"Usando mecanismo lento----------------");
returnValue = longWayFunc();
this.Redis.Set(key, returnValue);
private Dictionary<string, object> cacheMemory = new();
private Dictionary<string, object> lockKeys = new();
public void Set<T>(string key, T? objectToStore){
if(objectToStore == null) return;
cacheMemory[key.ToLowerInvariant()] = objectToStore;
public T Get<T>(string key)
key = key.ToLowerInvariant();
return this.cacheMemory.ContainsKey(key) ? (T)cacheMemory[key] : default;
private static object syncLoc = new Object();
public IDisposable Lock(string key,TimeSpan timeout, int retry)
key = key.ToLowerInvariant();
if(lockKeys.ContainsKey(key))
lockObject = lockKeys[key];
lockObject = new Object();
lockKeys[key] = lockObject;
return new LockScope(key, lockObject, timeout, retry);
public class LockScope : IDisposable
private readonly object _lockObject;
private readonly bool _lockAcquired;
public string Key { get; }
public TimeSpan Timeout { get; }
public int Retry { get; }
public LockScope(string key, object lockObject, TimeSpan timeout, int retry)
_lockObject = lockObject;
_lockAcquired = TryAcquireLock();
throw new TimeoutException($"Unable to acquire lock for key '{Key}' within the timeout period.");
private bool TryAcquireLock()
for (int i = 0; i < Retry; i++)
if (Monitor.TryEnter(_lockObject, Timeout))
Monitor.Exit(_lockObject);
namespace Infrastructure.Data
public class ProductRepository
{ Random rnd = new Random();
public decimal? GetPriceByProduct(int productId)
decimal? price = rnd.Next(1000);
int sleep = rnd.Next(500, 1500);
Console.WriteLine($"Entregando preço R$ {price}, com lentidão de {sleep}ms");
System.Threading.Thread.Sleep(sleep);