0
votes

Éviter ASYNC / Attendre si je sais que la plupart du temps, le résultat sera mis en cache

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 ASYNC / attendre code> et utiliser tâche.fromResult code> pour les valeurs en cache; p> xxx pré>

option 2, évitez 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();

        }


1 commentaires

L'option 1 ne compilera pas - vous devez soit supprimer l'ASYNC et l'attente d'attendre, soit supprimer la tâche .fromResult et renvoyer la valeur.


4 Réponses :


4
votes

L'approche officielle consiste à cacher la tâche , et non le t .

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 en cours . Si quelqu'un d'autre demande la valeur mise en cache avant la fin de la demande, ils sont également donnés à la même tâche en cours , et vous ne finissez pas de faire deux demandes.

Par exemple: xxx

Notez que vous devez penser à l'échec dans ce cas: si la demande à l'API échoue, vous mettrez en cache un Tâche qui contient une exception. Cela pourrait être ce que vous voulez, mais ce n'est peut-être pas.

Si, pour une raison quelconque, vous ne pouvez pas faire cela, le conseil officiel est d'utiliser valeurASTASS pour scénarios hautes performances. Ce type a des gotchas (comme vous ne pouvez pas l'attendre deux fois), donc je recommande Lecture de ce . Si vous n'avez pas d'exigences de performance élevées, tâche.fromResult est bien.


0 commentaires

-2
votes

Tout d'abord, cela appelle à la liaison du rant de vitesse:

https://ericlippert.com/2012/12/17/performance- Rant /

Les microoptimisations comme celles-ci sont généralement laissées au JIT. Ma règle echalique est que si vous vraiment besoin de cette différence, vous faites appel à une programmation en temps réel. Et pour la proramnition en temps réel, une poubelle collectée à la fois .NET était de manière proposée le mauvais environnement pour commencer. Quelque chose avec la gestion directe de la mémoire comme un code dangereux - même natif C ++ ou assembleur - aurait été meilleur.

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 Paresseux [t] ? Ou l'une des 5 classes différentes de la chape? (Comme avec la minuterie, il y en a une sur une technologie d'interface utilisateur spécifique).

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.


0 commentaires

3
votes

Votre premier ne fonctionnera pas. Le plus simple, et celui qui va aller pour la plupart du temps est le suivant: xxx

ou mieux encore si possible si possible: xxx

ceci fonctionne bien et retournera une tâche déjà complétée lorsque la valeur était dans le cache, donc attendre il continuera immédiatement.

Cependant, si la valeur est Dans la cache une grande partie du temps et la méthode est appelée assez souvent pour l'appareil extra vers async pour faire une différence alors vous pouvez l'éviter entièrement dans un tel cas: < / p> xxx

ici Si la valeur est mise en cache, nous évitons à la fois l'état-machine autour de async ainsi que la création d'une nouvelle tâche car nous avons enregistré une tâche réelle . Chacun d'entre eux ne se fait que dans le premier cas.


10 commentaires

Idéalement, vous souhaitez insérer la tâche en cours 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 . Ce serait un changement assez facile à utiliser à votre code. Cependant, avoir fait ce changement, le attend 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 a un attendre sur la première ligne, bien qu'il s'agissait d'une erreur de l'avoir publique au lieu de privé . Fixé.


@ Canton7 Notez que je stocke une tâche 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 complète 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)) est inutilement complexe par rapport à la tâche var = _api.getvalueeasync (clé); attendre la tâche; _Cache.add (clé, tâche);


@ 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 () 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.



1
votes

Ce que vous cherchez probablement est Mémoisation .

Une implémentation pourrait être quelque chose Comme ceci: xxx

source


0 commentaires