Générer une exception NullReferenceException tout en appelant la set_item méthode d'un objet Dictionary dans un multi-threading scénario
Notre site dispose d'une page de configuration tels que "config.aspx", lorsque la page de l'initialisation va charger quelques informations à partir d'un fichier de configuration. Pour mettre en cache le chargé de l'information, nous avons fourni une usine de classe et nous appelons une méthode publique de l'usine pour obtenir la configuration de l'instance lorsque la page est chargée. Mais parfois, quand le Pool d'Applications est redémarré, nous avons trouvé un message d'erreur dans le Journal des Événements tels que ci-dessous:
De Message: Objet de référence non définie à une instance d'un objet. Pile: au Système.Les Collections.Génériques.Dictionnaire`2.Insert(TKey clé, TValue valeur, Boolean add) au Système.Les Collections.Génériques.Dictionnaire`2.set_Item(TKey clé, TValue valeur) au ObjectFactory.GetInstance(string key) à la config.Page_Load(Object sender, EventArgs e) au Système.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, l'Objet o, t Objet, EventArgs e) au Système.Web.Util.CalliEventHandlerDelegateProxy.Rappel(Object sender, EventArgs e) au Système.Web.L'INTERFACE utilisateur.De contrôle.OnLoad(EventArgs e) au Système.Web.L'INTERFACE utilisateur.De contrôle.LoadRecursive() au Système.Web.L'INTERFACE utilisateur.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
L'usine de la classe implémente comme ci-dessous:
public static class ObjectFactory
{
private static object _InternalSyncObject;
private static Dictionary _Instances;
private static object InternalSyncObject
{
get
{
if (_InternalSyncObject == null)
{
var @object = new object();
Interlocked.CompareExchange(ref _InternalSyncObject, @object, null);
}
return _InternalSyncObject;
}
}
private static Dictionary Instances
{
get
{
if (_Instances == null)
{
lock (InternalSyncObject)
{
if (_Instances == null)
{
_Instances = new Dictionary();
}
}
}
return _Instances;
}
}
private static object LoadInstance(string key)
{
object obj = null;
//some statements to load an specific instance from a configuration file.
return obj;
}
public static object GetInstance(string key)
{
object instance;
if (false == Instances.TryGetValue(key, out instance))
{
instance = LoadInstance(key);
Instances[key] = instance;
}
return instance;
}
}
Je suppose que l'exception a été levée par la ligne "Cas[clé] = instance;", parce que c'est le seul code qui pourrait s'appeler set_Item
méthode d'un dictionnaire. Mais si le "Cas" de la valeur est null, il sera jeté une NullReferenceException
lors de l'appel de la TryGetValue
méthode et le cadre supérieur de la stacktrace devrait être le GetInstance
pas le Insert
. Personne ne sait comment le dictionnaire pourrait jeter un NullReferenceException
lors de l'appel de la set_Item
méthode multi-threading scénario?
Vous devez vous connecter pour publier un commentaire.
Que l'exception se produit à l'interne dans le
Dictionary
code, cela signifie que vous accédez à la mêmeDictionary
exemple à partir de plusieurs threads en même temps.Vous avez besoin de synchroniser le code dans le
GetInstance
méthode de sorte qu'un seul thread à la fois accède à laDictionary
.Edit:
Verrou autour de l'accès séparément, de sorte que vous n'êtes pas à l'intérieur d'une serrure tout en faisant de la (soi-disant) de temps de chargement:
Comme de .Net 4 vous avez ConcurrentDictionary qui est un dictionnaire thread-safe, pas besoin de plus "manuelle" de la synchronisation.
Pour citer http://msdn.microsoft.com/en-us/library/xfhwa508.aspx (italiques ajoutés par moi):
"Thread
Public static (Partagé en Visual Basic) les membres de ce type sont thread-safe. Tous les membres de l'instance ne sont pas garantis pour être thread-safe.
Un
Dictionary<(Of <(TKey, TValue>)>)
peut prendre en charge plusieurs lecteurs simultanément, aussi longtemps que la collection n'est pas modifiée. De même, l'énumération d'une collection n'est pas intrinsèquement une procédure thread-safe. Dans les rares cas où une énumération soutient avec accès en écriture, la collection doit être verrouillé au cours de l'ensemble de l'énumération. Pour permettre la collecte pour être accessible par plusieurs threads pour la lecture et l'écriture, vous devez mettre en place votre propre synchronisation."Je pense que votre
Instances
Dictionary
n'est pas nulle. L'exception est de l'intérieur de laInsert
méthode - sens qu'il y a unDictionary
objet au moment de l'exécution (d'ailleurs, comme vous l'avez dit, vous avez déjà euTryGetValue
avant, sur la même référence)Se pourrait-il que votre
key
est null?MODIFIER
Viens de vérifier elle -
TryGetValue
jette ArgumentNullException lorsqu'il reçoit une clé null, et donc n'insérez une clé null. Mais quelle classe êtes-vous à l'aide dans votre exemple? J'ai utilisé le génériqueIDictionary<string, string>
, mais je vois que vous êtes en utilisant un non générique. C'est une classe que vous avez hérité deDictionaryBase
ou peut-êtreHashTable
?Une meilleure solution serait de créer un synchronisé dictionnaire. En voici un qui va travailler dans cette situation. Le ReaderWriterLockSlim je pense que c'est le meilleur objet de synchronisation à utiliser dans cette situation. L'écriture pour le dictionnaire est assez rare. La plupart du temps, la clé sera dans le dictionnaire. Je n'ai pas implémenter toutes les méthodes du dictionnaire, simplement celles qui ont été utilisées dans le présent cas, de sorte qu'il n'est pas encore totalement synchronisé dictionnaire.