Frage

Ich habe eine ASP.NET-Anwendung mit einer Menge von dynamischen Inhalten. Der Inhalt ist das gleiche für alle Benutzer zu einem bestimmten Client gehören. Um die Anzahl der Datenbanktreffer benötigt pro Anfrage zu reduzieren, entschied ich mich Client-Level-Daten zwischenzuspeichern. Ich habe eine statische Klasse ( „client“), um die Daten zu halten.
Die meist häufig verwendete Methode der Klasse ist bei weitem „GetClientData“, die ein Clientdata-Objekt zurück bringt alle gespeicherten Daten für einen bestimmten Client enthält. Clientdata ist träge, obwohl geladen: wenn die angeforderte Client-Daten bereits im Cache gespeichert wird, erhält der Anrufer die im Cache gespeicherten Daten; andernfalls werden die Daten abgerufen, zu dem Cache hinzugefügt und dann an den Aufrufer zurückgegeben.

Schließlich begann ich auf der Linie unerwartete Abstürze in der GetClientData Methode bekommen, wo das Clientdata-Objekt in den Cache hinzugefügt wird. Hier ist die Methode Körper:

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;
}

Der Ausnahmetext ist immer so etwas wie „ein Objekt mit demselben Schlüssel ist bereits vorhanden.“ Natürlich habe ich versucht, den Code zu schreiben, so dass es war einfach nicht möglich, einen Client zum Cache hinzufügen, wenn es bereits existiert.

An diesem Punkt bin ich zu ahnen, dass ich eine Race-Bedingung haben und das Verfahren wird zweimal gleichzeitig ausgeführt werden, was erklären könnte, wie der Code zum Absturz bringen würde. Was ich bin verwirrt, obwohl, ist, wie das Verfahren überhaupt zweimal gleichzeitig ausgeführt werden kann. Soweit ich weiß, nur jede ASP.NET-Anwendung immer Felder eine Anforderung zu einem Zeitpunkt (das ist, warum wir HttpContext.Current verwenden können).

So ist dieser Fehler wahrscheinlich eine Race-Bedingung, dass Putting-Schlösser in kritischen Abschnitten benötigen? Oder bin ich ein offensichtlicher Fehler fehlt?

War es hilfreich?

Lösung

Wenn eine ASP.NET-Anwendung behandelt nur eine Anforderung zu einem Zeitpunkt alle ASP.NET-Websites in ernsthaften Schwierigkeiten sein würde. ASP.NET kann Dutzende zu einer Zeit (in der Regel 25 pro CPU-Kern) verarbeiten.

Sie sollten ASP.NET Cache verwenden Sie stattdessen Ihr eigenes Wörterbuch der Verwendung von Ihrem Objekt zu speichern. Operationen an dem Cache-Thread-sicher ist.

Beachten Sie sicher sein müssen, die den Betrieb auf dem Objekt, das Sie speichern im Cache sind THREAD, leider die meisten .NET-Klasse einfach die Instanz Mitglieder sind thread nicht zu-Punkt-Zustand zu lesen, ohne zu versuchen, die sein kann.

Bearbeiten :

Ein Kommentar zu dieser Antwort lautet: -

  

Nur atomare Operationen auf dem Cache sind Thread-sicher. Wenn Sie etwas tun, wie Scheck
  wenn ein Schlüssel vorhanden ist und es dann hinzufügen, das ist nicht Thread-sicher und kann den Artikel dazu führen,
  überschrieben.

Seine erwähnenswert, dass, wenn wir uns eine solche Operation atomar dann der Cache ist wahrscheinlich nicht der richtige Ort für die Ressource.

fühlen brauchen

Ich habe ziemlich viel Code, der genau das tut, wie der Kommentar beschreibt. Doch die Ressource gespeichert wird das gleiche in beiden Orten. Daher, wenn ein vorhandenes Element bei seltenen Gelegenheiten überschrieben wird nur die Kosten sind, dass ein Thread unnötig, eine Ressource erzeugt wird. Die Kosten für dieses seltene Ereignis ist viel weniger als die Kosten für versuchen, den Betrieb Atom jedes Mal ein Versuch zu machen, den Zugriff auf es gemacht wird.

Andere Tipps

Dies ist sehr einfach zu beheben:

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;
  }
}

Beachten Sie, dass, wenn Sie verwirren mit statischen Klassen, Sie haben das Potenzial für die Thread-Synchronisation Probleme. Wenn es eine statische Klasse-Ebene-Liste von einer Art (in diesem Fall _clients, das Dictionary Objekt), gibt es DEFINITELY geht Thread Synchronisierungsprobleme sein zu behandeln.

Ihr Code wirklich übernimmt nur ein Thread zu einer Zeit, in der Funktion ist.

Dies ist nur einfach in ASP.NET nicht wahr sein

Wenn Sie darauf bestehen, es auf diese Weise zu tun, verwenden Sie einen statischen Semaphore um diese Klasse den Bereich zu sperren.

Sie müssen die Threadsicherheit und minimieren Schloss.
 siehe doppelt geprüft Verriegelung ( http://en.wikipedia.org/wiki/Double-checked_locking )

Schreib einfach mit 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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top