Question

J'ai une application ASP.NET avec beaucoup de contenu dynamique. Le contenu est le même pour tous les utilisateurs appartenant à un client particulier. Pour réduire le nombre de visites de bases de données requises par demande, j'ai décidé de mettre en cache des données au niveau du client. J'ai créé pour contenir les données une classe statique ( « clientCache »).
La méthode la plus utilisée, souvent de la classe est de loin « GetClientData », qui ramène un objet contenant ClientData toutes les données stockées pour un client particulier. ClientData est chargé paresseusement, cependant: si les données client demandé est déjà mis en mémoire cache, l'appelant obtient les données en cache; sinon, les données sont extraites, ajouté au cache, puis renvoyé à l'appelant.

Finalement, j'ai commencé à obtenir des plantages intermittents dans la la méthode GetClientData sur la ligne où l'objet ClientData est ajouté au cache. Voici le corps de la méthode:

public static ClientData GetClientData(Guid fk_client)
{
    if (_clients == null)
        _clients = new Dictionary<Guid, ClientData>();

    ClientData client;
    if (_clients.ContainsKey(fk_client))
    {
        client = _clients[fk_client];
    }
    else
    {
        client = new ClientData(fk_client);
        _clients.Add(fk_client, client);
    }
    return client;
}

Le texte d'exception est toujours quelque chose comme « Un objet avec la même clé existe déjà. » Bien sûr, j'ai essayé d'écrire le code pour qu'il n'était pas possible d'ajouter un client dans le cache si elle existait déjà.

Je suis soupçonner que j'ai une condition de course et la méthode est en cours d'exécution deux fois simultanément, ce qui pourrait expliquer comment le code se briserait A ce stade,. Ce que je suis confus au sujet, cependant, est de savoir comment la méthode peut être exécutée deux fois simultanément à tous. Pour autant que je sache, toute application ASP.NET uniquement les champs jamais une demande à la fois (c'est la raison pour laquelle nous pouvons utiliser HttpContext.Current).

Alors, est-ce bug probablement une condition qui exigera mettre des verrous dans les sections critiques? Ou suis-je manque un bug plus évident?

Était-ce utile?

La solution

Si une application ASP.NET gère une seule requête à la fois tous les sites ASP.NET seraient en difficulté sérieuse. ASP.NET peut traiter des dizaines à la fois (typiquement 25 par noyau de CPU).

Vous devez utiliser ASP.NET au lieu du cache d'utiliser votre propre dictionnaire pour stocker votre objet. Les opérations sur le cache sont thread-safe.

Notez que vous devez être sûr que l'opération de lecture sur l'objet que vous stockez dans le cache sont threadsafe, malheureusement classe la plus .NET indiquer simplement les membres instance ne sont pas sans essayer de thread-safe pointer tout qui peut être.

Modifier :

Un commentaire à cette réponse Etats: -

  

Seules les opérations atomiques sur le cache sont thread-safe. Si vous faites quelque chose comme chèque
  si une clé existe et puis ajoutez, qui est pas thread-safe et peut causer l'élément à
  écrasées.

Il vaut la peine que si pointe vers nous pensons que nous devons faire une telle opération atomique alors le cache est probablement pas le bon endroit pour la ressource.

J'ai un peu de code qui fait exactement comme le commentaire décrit. Cependant la ressource stockée sera la même dans les deux endroits. Par conséquent, si un élément existant de rares occasions est écrasé le seul coût est qu'un fil généré inutilement une ressource. Le coût de cet événement rare est beaucoup moins que le coût d'essayer de faire l'opération atomique à chaque fois une tentative d'accès, il est fait.

Autres conseils

Ceci est très facile à corriger:

private _clientsLock = new Object();

public static ClientData GetClientData(Guid fk_client)
{
  if (_clients == null)
    lock (_clientsLock)
      // Check again because another thread could have created a new 
      // dictionary in-between the lock and this check
      if (_clients == null) 
        _clients = new Dictionary<Guid, ClientData>();

  if (_clients.ContainsKey(fk_client))
    // Don't need a lock here UNLESS there are also deletes. If there are
    // deletes, then a lock like the one below (in the else) is necessary
    return _clients[fk_client];
  else
  {
    ClientData client = new ClientData(fk_client);

    lock (_clientsLock)
      // Again, check again because another thread could have added this
      // this ClientData between the last ContainsKey check and this add
      if (!clients.ContainsKey(fk_client))
       _clients.Add(fk_client, client);

    return client;
  }
}

Gardez à l'esprit que chaque fois que vous salissez avec des classes statiques, vous avez la possibilité de problèmes de synchronisation de fil. S'il y a une liste de niveau de classe statique d'un certain type (dans ce cas, _clients, l'objet Dictionary), il y a DEFINITIF va être thread problèmes de synchronisation à traiter.

Votre code ne vraiment assumer qu'un seul thread est en fonction à la fois.

Ce sera tout simplement pas vrai dans ASP.NET

Si vous insistez pour le faire de cette façon, utilisez un sémaphores statique pour verrouiller la zone autour de cette classe.

vous avez besoin thread-safe et minimiser le blocage.
 voir verrouillage revérifié ( http://en.wikipedia.org/wiki/Double-checked_locking )

Il suffit d'écrire avec TryGetValue.


public static object lockClientsSingleton = new object();

public static ClientData GetClientData(Guid fk_client)
{
    if (_clients == null) {
        lock( lockClientsSingleton ) {
            if( _clients==null ) {
                _clients = new Dictionary``();
            }
        }
    }
    ClientData client;
    if( !_clients.TryGetValue( fk_client, out client ) )
    {
        lock(_clients) 
        {
            if( !_clients.TryGetValue( fk_client, out client ) ) 
            {
                client = new ClientData(fk_client)
                _clients.Add( fk_client, client );
            }
        }
    }
    return client;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top