Es scheint, als wollten Sie vermeiden, unnötigerweise zur Datenquelle zu wechseln. Wenn viele Threads zur gleichen Zeit dasselbe Objekt anfordern, möchten Sie es einem Benutzer ermöglichen, die Datenquelle abzufragen und die Daten zwischenzuspeichern und die anderen zu halten, bis die Daten gefüllt sind.
Möglicherweise möchten Sie eine haben decorator, um sicherzustellen, dass nur ein Abfragetyp zur gleichen Zeit ausgeführt wird. Sie können die Abfrage mit einem Objekt darstellen, das sich in einer Thread-sicheren Auflistung befindet, und sie während der Ausführung der Abfrage sperren.
So eine Schnittstelle zum Ausdruck, wie Sie Ihre Datenquelle abfragen gegeben:
interface IQueryExecuter<TQuery, TResult>
{
TResult Execute(TQuery query);
}
Sie einen Thread sicher Dekoratorobjekt verwenden können, die Abfragen Ergebnisse und im Falle speichert das Abfrageergebnis nicht im Cache gespeichert wird, wird nur ein Thread führt die Abfrage an die Datenquelle durch:
Nicht getesteter Code!
class QueryThrottler<TQuery, TResult> : IQueryExecuter<TQuery, TResult>
{
// do not lock on external objects
class QueryObject
{
public TQuery Query { get; set; }
}
readonly IQueryExecuter<TQuery, TResult> _inner;
readonly ConcurrentDictionary<TQuery, QueryObject> _queries;
public QueryThrottler(IQueryExecuter<TQuery, TResult> inner)
{
_queries = new ConcurrentDictionary<TQuery, QueryObject>();
_inner = inner;
}
public TResult Execute(TQuery query)
{
// if it is on cache return the result
TResult result;
if (!IsCached(query, out result))
{
// otherwise lock other threads
// on the same query
var queryObject = _queries.GetOrAdd(query, k => new QueryObject() { Query = k });
lock (queryObject)
{
// double check it is not cached already
if (!IsCached(query, out result))
{
result = _inner.Execute(queryObject.Query);
PopulateCache(query, result);
}
}
}
return result;
}
private void PopulateCache(TQuery query, TResult result)
{
// Save the result in Redis using TQuery as key
}
private bool IsCached(TQuery query, out TResult result)
{
// go to redis and check if the query is cached using TQuery as key
// if exists, set the result out parameter and return true
// otherwise, return false
result = default(TResult);
return false;
}
}
Dieser Code stützt sich auf TQuery richtige Implementierungen von GetHashCode
und Equals
haben.
Das verzierte Objekt (inner
im Konstruktor) ist das Objekt, das die eigentliche Abfrage an die Datenquelle ausführen würde.
Wenn Sie viele Server haben, und wollen sicherstellen, dass nur ein Thread machen von allen Servern die eigentliche Abfrage mit der Datenquelle zu tun, anstatt lock
, können Sie eine verteilte Sperre wie LockTake/LockRelease von StackExchange.Redis verwenden.