Share via


Verfahren zum Zwischenspeichern von Objekten

Letzte Änderung: Sonntag, 17. Januar 2010

Gilt für: SharePoint Foundation 2010

Viele Entwickler verwenden die Cacheobjekte von Microsoft .NET Framework (beispielsweise System.Web.Caching.Cache), um eine bessere Speicherauslastung zu erzielen und die Gesamtleistung des Systems zu verbessern. Viele dieser Objekte sind jedoch nicht "threadsicher", und durch das Zwischenspeichern von Objekten können Anwendungsfehler verursacht werden bzw. unerwartete oder beziehungslose Benutzerfehler auftreten.

HinweisHinweis

Die im vorliegenden Abschnitt erläuterten Zwischenspeicherverfahren unterscheiden sich von den benutzerdefinierten Zwischenspeicheroptionen für Web Content Management, die in Benutzerdefinierte Zwischenspeicherung (Übersicht) erläutert werden.

Zwischenspeichern von Daten und Objekten

Das Zwischenspeichern stellt eine ausgezeichnete Möglichkeit zum Verbessern der Systemleistung dar. Sie müssen jedoch die Vorteile des Zwischenspeicherns und die Notwendigkeit der Threadsicherheit gegeneinander abwägen, da einige SharePoint-Objekte nicht threadsicher sind und ihre Zwischenspeicherung ein unerwartetes Verhalten nach sich zieht.

Zwischenspeichern von nicht threadsicheren SharePoint-Objekten

Sie können versuchen, Leistung und Speicherauslastung durch das Zwischenspeichern von SPListItemCollection-Objekten zu verbessern, die von Abfragen zurückgegeben werden. Dies ist im Allgemeinen empfehlenswert. Das SPListItemCollection-Objekt enthält jedoch ein eingebettetes SPWeb-Objekt, das nicht threadsicher ist und daher nicht zwischengespeichert werden sollte.

Angenommen, das SPListItemCollection-Objekt wird in einem Thread zwischengespeichert. Da andere Threads versuchen, dieses Objekt zu lesen, kann ein Anwendungsfehler auftreten, oder die Anwendung kann ein ungewöhnliches Verhalten aufweisen, da das eingebettete SPWeb-Objekt nicht threadsicher ist. Weitere Informationen zum SPWeb-Objekt und zur Threadsicherheit finden Sie unter der Microsoft.SharePoint.SPWeb-Klasse.

In der Anleitung im folgenden Abschnitt wird beschrieben, wie Sie Probleme beim Zwischenspeichern von nicht threadsicheren SharePoint-Objekten in einer Multithreadumgebung vermeiden können.

Grundlegendes zu den möglichen Nachteilen der Threadsynchronisierung

Möglicherweise ist Ihnen überhaupt nicht bewusst, dass Ihr Code in einer Multithreadumgebung ausgeführt wird (so wird z. B. Internetinformationsdienste bzw. IIS standardmäßig in mehreren Threads ausgeführt), und Sie verfügen über keine Kenntnisse in Bezug auf das Verwalten einer solchen Umgebung. Im folgenden Beispiel wird Code veranschaulicht, mit dem gelegentlich nicht threadsichere Microsoft.SharePoint.SPListItemCollection-Objekte zwischengespeichert werden.

Codierungsverfahren, von denen abgeraten wird

Zwischenspeichern eines Objekts, das von mehreren Threads gelesen werden kann

public void CacheData()
{
   SPListItemCollection oListItems;

   oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
   if(oListItems == null)
   {
      oListItems = DoQueryToReturnItems();
      Cache.Add("ListItemCacheName", oListItems, ..);
   }
}
Public Sub CacheData()
    Dim oListItems As SPListItemCollection

    oListItems = CType(Cache("ListItemCacheName"), SPListItemCollection)
    If oListItems Is Nothing Then
        oListItems = DoQueryToReturnItems()
        Cache.Add("ListItemCacheName", oListItems,..)
    End If
End Sub

Die Verwendung des Caches im vorherigen Beispiel ist funktional korrekt. Da das ASP.NET-Cacheobjekt jedoch threadsicher ist, kann dies mögliche Leistungsprobleme nach sich ziehen. (Weitere Informationen zum Zwischenspeichern in ASP.NET finden Sie unter der Cache-Klasse.) Wenn die Ausführung der Abfrage im obigen Beispiel 10 Sekunden dauert, greifen möglicherweise während dieses Zeitraums viele Benutzer auf diese Seite zu. In diesem Fall würden alle Benutzer dieselbe Abfrage ausführen, wobei dasselbe Cacheobjekt aktualisiert würde. Wenn diese Abfrage zehn-, fünfzig- oder hundertmal ausgeführt wird und gleichzeitig mehrere Threads versuchen, ein und dasselbe Objekt zu aktualisieren (insbesondere auf Hyperthreadcomputern mit vielen Prozessen), wird die Leistung besonders schwerwiegend beeinträchtigt.

Wenn Sie verhindern möchten, dass durch mehrere Abfragen gleichzeitig auf dieselben Objekte zugegriffen wird, müssen Sie den Code wie folgt ändern.

Anwenden einer Sperre

Überprüfen auf NULL

private static object _lock =  new object();

public void CacheData()
{
   SPListItemCollection oListItems;

   lock(_lock) 
   {
      oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
      if(oListItems == null)
      {
         oListItems = DoQueryToReturnItems();
         Cache.Add("ListItemCacheName", oListItems, ..);
     }
   }
}
Private Shared _lock As New Object()

Public Sub CacheData()
    Dim oListItems As SPListItemCollection

    SyncLock _lock
        oListItems = CType(Cache("ListItemCacheName"), SPListItemCollection)
        If oListItems Is Nothing Then
            oListItems = DoQueryToReturnItems()
 Cache.Add("ListItemCacheName", oListItems,..)
        End If
    End SyncLock
End Sub

Sie können die Leistung geringfügig erhöhen, indem Sie die Sperre im Codeblock if(oListItems == null) festlegen. Dadurch müssen Sie nicht sämtliche Threads anhalten, während Sie überprüfen, ob die Daten bereits zwischengespeichert sind. Je nachdem, wie lange die Rückgabe der Daten durch die Abfrage dauert, kann die Abfrage immer noch von mehreren Benutzern gleichzeitig ausgeführt werden. Dies gilt insbesondere für die Ausführung auf Computern mit vielen Prozessoren. Beachten Sie Folgendes: Je größer die Anzahl der ausgeführten Prozessoren und je größer die Dauer der Abfrage, desto wahrscheinlicher verursacht die festgelegte Sperre im Codeblock if() Probleme. Um sicherzustellen, dass kein anderer Thread oListItems erstellt, bevor der Codeblock durch den aktuellen Thread bearbeitet werden kann, können Sie folgendes Muster verwenden.

Anwenden einer Sperre

Erneutes Überprüfen auf NULL

private static object _lock =  new object();

public void CacheData()
{
   SPListItemCollection oListItems;
       oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
      if(oListItems == null)
      {
         lock (_lock) 
         {
              // Ensure that the data was not loaded by a concurrent thread 
              // while waiting for lock.
              oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
              if (oListItems == null)
              {
                   oListItems = DoQueryToReturnItems();
                   Cache.Add("ListItemCacheName", oListItems, ..);
              }
         }
     }
}
Private Shared _lock As New Object()

Public Sub CacheData()
    Dim oListItems As SPListItemCollection
    oListItems = CType(Cache("ListItemCacheName"), SPListItemCollection)
    If oListItems Is Nothing Then
        SyncLock _lock
            ' Ensure that the data was not loaded by a concurrent thread 
            ' while waiting for lock.
            oListItems = CType(Cache("ListItemCacheName"), SPListItemCollection)
            If oListItems Is Nothing Then
                oListItems = DoQueryToReturnItems()
                           Cache.Add("ListItemCacheName", oListItems,..)
            End If
        End SyncLock
    End If
End Sub

Wenn der Cache bereits gefüllt ist entspricht die Leistung aus dem obigen Beispiel der der Anfangsimplementierung. Wenn der Cache nicht gefüllt und das System nur geringfügig ausgelastet ist, bewirkt die Sperre eine leichte Beeinträchtigung der Leistung. Durch diesen Ansatz kann die Leistung des Systems bei starker Auslastung beträchtlich gesteigert werden, da die Abfrage nur einmal und nicht mehrere Male ausgeführt wird (und Abfragen im Gegensatz zur Synchronisierung normalerweise erheblich zur Auslastung beitragen).

Der Code in diesen Beispielen hält alle anderen Threads in einem kritischen, in IIS ausgeführten Abschnitt an. Dabei wird verhindert, dass andere Threads auf das zwischengespeicherte Objekt zugreifen, bevor es vollständig erstellt wurde. Dadurch wird das Problem der Threadsynchronisierung behoben. Der Code ist jedoch immer noch nicht korrekt, da ein nicht threadsicheres Objekt zwischengespeichert wird.

Das Problem mit der Threadsicherheit wird behoben, indem Sie ein DataTable-Objekt zwischenspeichern, das aus dem SPListItemCollection-Objekt erstellt wird. Ändern Sie das obige Beispiel wie folgt, sodass Ihr Code die Daten aus dem DataTable-Objekt abruft.

Empfehlenswerte Codierungsverfahren

Zwischenspeichern eines DataTable-Objekts

private static object _lock =  new object();

public void CacheData()
{
   DataTable oDataTable;
   SPListItemCollection oListItems;
   lock(_lock)
   {
           oDataTable = (DataTable)Cache["ListItemCacheName"];
           if(oDataTable == null)
           {
              oListItems = DoQueryToReturnItems();
              oDataTable = oListItems.GetDataTable();
              Cache.Add("ListItemCacheName", oDataTable, ..);
           }
   }
}
Private Shared _lock As New Object()

Public Sub CacheData()
    Dim oDataTable As DataTable
    Dim oListItems As SPListItemCollection
    SyncLock _lock
        oDataTable = CType(Cache("ListItemCacheName"), DataTable)
        If oDataTable Is Nothing Then
            oListItems = DoQueryToReturnItems()
            oDataTable = oListItems.GetDataTable()
            Cache.Add("ListItemCacheName", oDataTable,..)
        End If
    End SyncLock
End Sub

Weitere Informationen und Beispiele zum Verwenden des DataTable-Objekts sowie weitere nützliche Empfehlungen zum Entwickeln von SharePoint-Anwendungen finden Sie im Referenzthema zur DataTable-Klasse.