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.
![]() |
---|
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.