Lequel de ceux-ci est la meilleure option lorsque vous travaillez avec la mise en cache en C #?
Je suis intéressé par le niveau de compilateur lequel de ceux-ci est la solution la plus élégante / performante. p>
par exemple, le compilateur .NET utilise des astuces pour savoir que lorsque le code s'exécutera de manière synchrone et évitez de créer / exécuté ASYNC inutile Await le code? P>
Option 1, Utilisez option 2, évitez ASYNC / attendre code> et utiliser
tâche.fromResult code> pour les valeurs en cache; p>
async / attendre code> et utilisez quelque chose comme
getawaiter (). getResult () code> Pour les quelques fois, le point d'extrémité de l'API sera touché. P>
public T GetValue<T>(string key)
{
if (_cache.containsKey(key))
{
// 99% of the time will hit this
return _cache.GetItem(key);
}
return _api.GetValue(key).GetAwaiter().GetResult();
}
4 Réponses :
L'approche officielle consiste à cacher la tâche Ceci a également l'avantage que si quelqu'un demande la valeur, vous Peut lancer la demande d'extraction de la valeur, puis mettre en cache la tâche Par exemple: p> Notez que vous devez penser à l'échec dans ce cas: si la demande à l'API échoue, vous mettrez en cache un Si, pour une raison quelconque, vous ne pouvez pas faire cela, le conseil officiel est d'utiliser
t code>.
en cours
en cours
Tâche code> qui contient une exception. Cela pourrait être ce que vous voulez, mais ce n'est peut-être pas. P>
valeurASTASS
Tout d'abord, cela appelle à la liaison du rant de vitesse: p>
https://ericlippert.com/2012/12/17/performance- Rant / P>
Les microoptimisations comme celles-ci sont généralement laissées au JIT. Ma règle echalique est que si vous Deuxièmement, une tâche pourrait simplement être le mauvais outil ici. Peut-être que ce que vous voulez réellement, c'est quelque chose comme Il est possible d'utiliser n'importe quel outil à plusieurs fins. Mais les tâches sont pour le multitâche et il existe de meilleurs outils pour la mise en cache et l'initialisation paresseuse. Et paresseux [t] est même intrinsèquement thread Save. P>
Votre premier ne fonctionnera pas. Le plus simple, et celui qui va aller pour la plupart du temps est le suivant: ou mieux encore si possible si possible: p> ceci fonctionne bien et retournera une tâche déjà complétée lorsque la valeur était dans le cache, donc Cependant, si strong> la valeur est Dans la cache une grande partie du temps ici Si la valeur est mise en cache, nous évitons à la fois l'état-machine autour de attendre code> il continuera immédiatement. p>
async code> pour faire une différence alors vous pouvez l'éviter entièrement dans un tel cas: < / p>
async code> ainsi que la création d'une nouvelle tâche
code> car nous avons enregistré une tâche
réelle code>. Chacun d'entre eux ne se fait que dans le premier cas. P> p>
Idéalement, vous souhaitez insérer la tâche code> en cours code> dans le cache avant de l'attendre. De cette façon, si quelqu'un d'autre demande la valeur du cache avant la demande, ils obtiennent la même tâche Code>. Ce serait un changement assez facile à utiliser à votre code. Cependant, avoir fait ce changement, le
attend code> devient inutile, et vous pourriez aussi bien supprimer toutes les machines ASYNC.
@ Canton7 Il est préférable de ne pas frapper le cache deux fois. Et getandcachevalueasync code> a un
attendre code> sur la première ligne, bien qu'il s'agissait d'une erreur de l'avoir
publique code> au lieu de
privé code> . Fixé.
@ Canton7 Notez que je stocke une tâche code> code> dans le cache de la dernière version.
S'il y a deux demandes pour le même article, où la seconde se passe avant la première fois, vous souhaitez toucher le cache deux fois, et ne pas effectuer la demande coûteuse de la valeur deux fois.
Dans votre dernier exemple, il n'y a aucun avantage à stocker une tâche code> complète code> dans le cache. Vous n'ajoutez que des machines asynchrones inutiles sans gain.
@ Canton7, je veux dire que cela ne frappe pas une fois de voir si c'est présent et une seconde fois pour le trouver. Dans le dernier exemple, je ne stocke pas une tâche s'il y en a un dans le cache déjà. (Il peut être utile de stocker une tâche tout en cours, mais cela complique des questions si la tâche pouvait échouer, car à moins que cet échec ne se produise toujours alors que nous devrions la sortir de la mémoire cache).
C'est un point de justice à l'échec, et je vais l'ajouter à ma réponse. Mais dans votre dernier échantillon, je maintiens toujours que résultat = attendre _api.getvalueeasync (clé); _Cache.add (clé, tâche.fromResult (résultat)) code> est inutilement complexe par rapport à la tâche
var = _api.getvalueeasync (clé); attendre la tâche; _Cache.add (clé, tâche); code>
@ canton7 vrai. Un oubli. Mise à jour.
Merci pour la discussion!
@ canton7 aussi. Bien sûr, parfois, il faut faire plus de travail pour obtenir le résultat que d'attendre une autre tâche, auquel cas le DRESULT () code> est nécessaire. De même, on veut que le cache fonctionne pour la synchronisation et l'asynchronisation, dans ce que la tâche stocke la tâche a une inconvénient.
Ce que vous cherchez probablement est Mémoisation .
Une implémentation pourrait être quelque chose Comme ceci: p> source p> p>
L'option 1 ne compilera pas - vous devez soit supprimer l'ASYNC et l'attente d'attendre, soit supprimer la tâche
.fromResult code> et renvoyer la valeur.