J'ai la fonction suivante qui est destinée à "mémoialiser" des fonctions sans argument. Ce qui signifie seulement appeler la fonction une fois, puis renvoyer le même résultat tous les autres moments.
private static Func<T> Memoize<T>(Func<T> func)
{
var lockObject = new object();
var value = default(T);
var inited = false;
return () => {
if (inited)
return value;
lock (lockObject) {
if (!inited) {
value = func();
inited = true;
}
}
return value;
};
}
3 Réponses :
Non, car S'il n'y a pas de clôture de libération avant Ceci est, à la manière, une instance du motif de verrouillage à double vérification déjà très bien documenté. P> Au lieu d'utiliser une Lambda qui capture les variables locales (qui permettra au compilateur générer une classe implicite pour contenir les variables fermées dans des champs non volatils), je suggère de créer explicitement votre propre classe avec un inité code> n'est pas volatile code>. volatile code> vous donne la libération de la mémoire et acquérir des clôtures dont vous avez besoin afin d'établir le bon arrive-avant em> relation. initée est défini sur true, puis la valeur code> ne peut pas être complètement écrite à l'heure à laquelle un autre thread lit initéd code> et le voit comme vrai, ce qui pourrait entraîner une demi-personne. objet construit étant retourné. De même, s'il y a une clôture de libération, mais aucune clôture d'acquérir correspondante avant de lire initée code> dans le premier chèque, il est possible que l'objet est complètement construit, mais que le noyau de la CPU qui a vu initée code > Comme vrai n'a pas encore vu les effets de mémoire de la valeur code> étant écrits (la cohérence du cache ne nécessite pas nécessairement que les effets des écrivies consécutives sont vus dans l'ordre sur d'autres cœurs). Cela entraînerait une fois potentiellement un objet à moitié construit étant renvoyé. P> volatile code> Classé pour Valeur Code>. P> private class Memoized<T>
{
public T value;
public volatile bool inited;
}
private static Func<T> Memoize<T>(Func<T> func)
{
var memoized = new Memoized<T>();
return () => {
if (memoized.inited)
return memoized.value;
lock (memoized) {
if (!memoized.inited) {
memoized.value = func();
memoized.inited = true;
}
}
return memoized.value;
};
}
Je vais simplement le remplacer par une instance de paresseux: Func statique privé
@Notnull, à propos d'être difficile à obtenir de droite: Changer Nouveau paresseux Nouveau paresseux
Non, ce code n'est pas sûr. Le compilateur est libre de réorganiser les écrivies sur Ce modèle est appelé à double coché Verrouillage et est discuté par Albahari sous initialisation paresseuse . La solution recommandée consiste à utiliser la classe intégrée valeur code> et inité code>; Donc, le système de mémoire est donc. Cela signifie qu'un autre thread pourrait voir inité code> défini sur true code> tandis que la valeur code> est toujours à sa valeur par défaut. lazy
Je pense que vous feriez mieux d'utiliser la norme paresseux pour implémenter les fonctionnalités dont vous avez besoin, comme dans:
Il semble que le constructeur par défaut (sans Boolean) soit également en sécurité. Selon les documents: "Le mode de sécurité du fil d'une instance paresseux
@Notnull Oui, mais l'utiliser explicitement dans ce cas fait pour une bonne documentation de cette ligne
Pourquoi n'utilisez-vous pas l'intégré < Code> paresseux code> classe ?
Avec votre code, il peut arriver que Func () soit évalué deux fois si Memoize () est appelé avant le premier Func () retourné.
Ce n'est pas un duplicata. Le problème est le même, mais la solution est différente (ce n'est pas un singleton!).
Ce particulier Réponse dans le duplicata dit "OK", mais vous devriez en effet envisager d'utiliser l'utilisation existante
paresseux code> faire cela.@Dkoch: Non, cela ne peut pas arriver. Pour que cela se produise,
inité code> devrait être considéré comme faux à l'intérieur i> la serrure par deux threads distincts, ce qui est clairement impossible (saufFunc code> lancers).@Cameron La question que j'ai choisie n'est pas sur le singleton non plus - Verrouillage de la double vérification est le motif utilisé dans les deux questions ... Je suis disposé à ré-ouvrir cette question si quelqu'un peut le mettre à jour pour clarifier pourquoi il n'est pas associé au verrouillage à double vérité.
@ALEXEI: True, mais je pense que l'utilisation du modèle ici est suffisamment spécifique pour justifier sa propre question (appliquer les réponses à cette question à celle-ci nécessite un saut non insignifiant).
J'ai rouvert. L'autre question a demandé si le modèle de verrouillage de la double vérification dans .NET est cassé (ce n'est pas); Cette question demande si son code l'applique correctement. Liés, mais pas en double.
@Douglas - Bon point ... Étrange que personne n'a essayé de mettre en œuvre cela avant :)
Notnull, s'il vous plaît voir si mon édition s'aligne sur vos objectifs sur la question.
@Alexeilevenkov Mon objectif était en effet de recevoir des commentaires sur la mise en œuvre.
@Alexeilevenkov: Cette question introduit de nouvelles considérations qui ne s'appliquaient pas au scénario général. Étant donné que les variables doivent être déclarées comme des locaux (plutôt que des champs), ils ne peuvent pas tirer parti du mot clé volatile.