2015-10-08 13 views
5

abgeschlossen Die gleiche AccountId wird den Anruf fast gleichzeitig tätigen (sie werden alle mit einer Übertragung benachrichtigt).Pause gleichzeitiger REST ruft, bis erste

In der Methode wird die Ergebnismenge für 10 Sekunden zwischengespeichert, da ein Nahzeitergebnis für alle Personen, die die Anforderung innerhalb dieses Fensters stellen, in Ordnung ist. Da jedoch alle gleichzeitig den Anruf tätigen (wiederum für eine bestimmte AccountID), wird der Cache nie von vorn besetzt, so dass jeder den Datenbankaufruf durchführt.

Also meine Frage ist, innerhalb der Methode, wie kann ich alle eingehenden Anfragen für eine bestimmte accountId Pause und sie alle für das erste Ergebnis warten vervollständigen gesetzt, so dass der Rest der Anrufe, die im Cache gespeicherten verwenden können Ergebnismenge?

Ich habe ein wenig über Monitor.Pulse und Monitor.Lock gelesen, aber die Implementierung für eine per-AccountId Sperre entgeht mir irgendwie. Jede Hilfe würde sehr geschätzt werden.

+0

gibt es einen Grund, warum viele Benutzer die gleiche AccountId verwenden, ist, dass ein Dienstkontonummer ..? Können Sie Ihre gespeicherte Prozedur ändern, um Transaktionen zu verwenden oder einen 'With No Lock'-Befehl an die Datenbank hinzuzufügen, vorausgesetzt, Sie verwenden Sql Server ..? – MethodMan

+0

Ich würde einen 2-Level-Cache, also wenn Sie einen zweiten Cache von "ausstehend", dass die letzten 10 20 Sekunden dann alle Anrufe, die derzeit in der schwebenden Cache sind selbst Blockierung und warten ein bisschen, bevor Sie den tatsächlichen db-Aufruf. Sie können aus einem ähnlichen Grund ähnlich "bewiesen nicht existieren" zwischenspeichern. Ich würde Monitor und Sperre basierend auf dem Wert der Variablen vermeiden. Ich würde diese Sperren und Synchronisierer für Code und Speicher reservieren, unabhängig vom Wert der Variablen. –

Antwort

2

Sie müssen dasselbe Objekt für Anfragen mit derselben AccountId sperren, aber für jede einzelne AccountId ein anderes Objekt verwenden. Hier finden Sie ein Beispiel für die Verwendung des Dictionary, um das Sperrobjekt für einzelne AccountIds zu verfolgen.

Dictionary<int, Object> Locks = new Dictionary<int, object>(); 

    List<item> GetItems(int AccountID) 
    { 
     //Get different lock object for each AccountId 
     Object LockForAccountId = GetLockObject(AccountID); 

     //block subsequent threads until first thread fills the cache 
     lock (LockForAccountId) 
     { 
      var x = getFromCache(AccountID); 
      if (x == null) 
      { 
       x = getFromDatabase(AccountID); 
       addToCache(AccountID, x); 
      } 
      return x; 
     } 
    } 

    private Object GetLockObject(int AccountID) 
    { 
     Object LockForAccountId; 

     //we must use global lock while accessing dictionary with locks to prevent multiple different lock objects to be created for the same AccountId 
     lock (Locks) 
     { 
      if (!Locks.TryGetValue(AccountID, out LockForAccountId)) 
      { 
       LockForAccountId = new Object(); 
       Locks[AccountID] = LockForAccountId; 
      } 
     } 
     return LockForAccountId; 
    } 
0

Haben Sie darüber nachgedacht Lazy<T> für diese Verwendung?

Versuchen Sie diesen Code:

private object _gate = new object(); 
List<item> GetItems(int AccountID) 
{ 
    lock (_gate) 
    { 
     var x = getFromCache(AccountID); 
     if (x == null) 
     { 
      x = new Lazy<List<item>>(() => getFromDatabase(AccountID)); 
      addToCache(AccountID, x); 
     } 
     return x.Value; 
    } 
} 

Sie müssten getFromCache & addToCache ändern folgende Unterschriften haben:

Lazy<List<item>> getFromCache(int AccountID) 
void addToCache(int AccountID, Lazy<List<item>> x)