System.Threading.ReaderWriterLockSlim-Klasse
Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.
Dient ReaderWriterLockSlim zum Schutz einer Ressource, die von mehreren Threads gelesen und jeweils von einem Thread in einen Thread geschrieben wird. ReaderWriterLockSlim ermöglicht es mehreren Threads, sich im Lesemodus zu befinden, ein Thread im Schreibmodus mit exklusivem Besitz der Sperre zu sein und einen Thread mit Lesezugriff im Upgrademodus zu verwenden, aus dem der Thread auf den Schreibmodus aktualisieren kann, ohne den Lesezugriff auf die Ressource zurückzugeben.
Hinweis
- ReaderWriterLockSlim ähnelt ReaderWriterLock, verfügt aber über vereinfachte Regeln für Rekursion sowie für Upgrade und Downgrade des Sperrstatus. ReaderWriterLockSlim vermeidet viele potenzielle Deadlocks. Darüber hinaus ist die Leistung von ReaderWriterLockSlim wesentlich besser als die von ReaderWriterLock. ReaderWriterLockSlim wird für alle Neuentwicklungen empfohlen.
- ReaderWriterLockSlim ist nicht threadabbruchsicher. Sie sollten sie nicht in einer Umgebung verwenden, in der Threads, die darauf zugreifen, abgebrochen werden können, z. B. .NET Framework. Wenn Sie .NET Core oder .NET 5+ verwenden, sollte dies in Ordnung sein. Abort wird in .NET Core nicht unterstützt und ist in .NET 5 und höheren Versionen veraltet .
Standardmäßig werden neue Instanzen mit ReaderWriterLockSlim dem LockRecursionPolicy.NoRecursion Flag erstellt und lassen keine Rekursion zu. Diese Standardrichtlinie wird für alle neuen Entwicklungen empfohlen, da rekursion unnötige Komplikationen einführt und Ihren Code anfälliger für Deadlocks macht. Um die Migration von vorhandenen Projekten zu vereinfachen, die verwendet Monitor werden, oder ReaderWriterLockverwenden Sie das LockRecursionPolicy.SupportsRecursion Flag zum Erstellen von Instanzen, die ReaderWriterLockSlim rekursion zulassen.
Ein Thread kann die Sperre in drei Modi eingeben: Lesemodus, Schreibmodus und upgradefähiger Lesemodus. (Im restlichen Thema wird "Upgradeable Read Mode" als "upgradeabler Modus" bezeichnet, und der Ausdruck "Eingabemodus x
" wird vor dem längeren Ausdruck "In den Sperrmodus x
eingeben" verwendet.)
Unabhängig von der Rekursionsrichtlinie kann immer nur ein Thread im Schreibmodus ausgeführt werden. Wenn sich ein Thread im Schreibmodus befindet, kann kein anderer Thread die Sperre in einen beliebigen Modus eingeben. Es kann jeweils nur ein Thread im Upgrademodus ausgeführt werden. Eine beliebige Anzahl von Threads kann sich im Lesemodus befinden, und es kann einen Thread im Upgrademodus geben, während sich andere Threads im Lesemodus befinden.
Wichtig
Dieser Typ implementiert die IDisposable-Schnittstelle. Nach Abschluss der Verwendung sollten Sie den Typ entweder direkt oder indirekt löschen. Zum direkten Löschen des Typs rufen Sie seine Dispose-Methode in einem try
/catch
-Block auf. Zum indirekten Löschen verwenden Sie ein Sprachkonstrukt wie using
(in C#) oder Using
(in Visual Basic). Weitere Informationen finden Sie im Abschnitt „Verwenden eines Objekts, das IDisposable implementiert“ des Themas „Die IDisposable-Schnittstelle“.
ReaderWriterLockSlim hat die Threadaffinität verwaltet; d. h., jedes Thread Objekt muss eigene Methodenaufrufe ausführen, um den Sperrmodus ein- und auszuschalten. Kein Thread kann den Modus eines anderen Threads ändern.
Wenn eine ReaderWriterLockSlim Rekursion nicht zulässt, kann ein Thread, der versucht, die Sperre einzugeben, aus mehreren Gründen blockieren:
Ein Thread, der versucht, in den Lesemodus zu wechseln, wenn Threads vorhanden sind, die auf den Schreibmodus warten oder ein einzelner Thread im Schreibmodus vorhanden ist.
Hinweis
Das Blockieren neuer Leser, wenn Autoren in die Warteschlange gestellt werden, ist eine Sperr-Fairness-Richtlinie, die Autoren bevorzugt. Die aktuelle Fairnesspolitik gleicht Fairness mit Lesern und Autoren ab, um den Durchsatz in den am häufigsten verwendeten Szenarien zu fördern. Zukünftige Versionen von .NET können neue Fairnessrichtlinien einführen.
Ein Thread, der versucht, upgradefähige Modus zu öffnen, blockiert, wenn bereits ein Thread im upgradefähigen Modus vorhanden ist, wenn Threads warten, um in den Schreibmodus zu wechseln, oder wenn ein einzelner Thread im Schreibmodus vorhanden ist.
Ein Thread, der versucht, Schreibmodusblöcke einzugeben, wenn ein Thread in einem der drei Modi vorhanden ist.
Upgrade- und Downgradesperren
Der upgradefähige Modus ist für Fälle gedacht, in denen ein Thread in der Regel aus der geschützten Ressource liest, aber ggf. schreiben muss, wenn eine Bedingung erfüllt ist. Ein Thread, der in einen ReaderWriterLockSlim upgradefähigen Modus eingegeben hat, hat Lesezugriff auf die geschützte Ressource und kann durch Aufrufen der Methoden TryEnterWriteLock auf den EnterWriteLock Schreibmodus aktualisiert werden. Da es jeweils nur einen Thread im upgradefähigen Modus geben kann, kann das Upgrade auf den Schreibmodus nicht inaktiv sein, wenn rekursion nicht zulässig ist, was die Standardrichtlinie ist.
Wichtig
Unabhängig von der Rekursionsrichtlinie ist ein Thread, der zunächst in den Lesemodus wechselt, nicht in den upgradefähigen Modus oder schreibmodus zu aktualisieren, da dieses Muster eine starke Wahrscheinlichkeit von Deadlocks erzeugt. Wenn z. B. zwei Threads im Lesemodus beide versuchen, in den Schreibmodus zu wechseln, werden sie inaktiv. Der upgradefähige Modus wurde entwickelt, um solche Deadlocks zu vermeiden.
Wenn andere Threads im Lesemodus vorhanden sind, wird der Thread, der Blöcke aktualisiert, ausgeführt. Während der Thread blockiert ist, werden andere Threads, die versuchen, in den Lesemodus zu wechseln, blockiert. Wenn alle Threads aus dem Lesemodus beendet wurden, wechselt der blockierte upgradefähige Thread in den Schreibmodus. Wenn andere Threads auf den Schreibmodus warten, werden sie erneut blockiert Standard, da der einzelne Thread, der sich im upgradefähigen Modus befindet, verhindert, dass sie exklusiven Zugriff auf die Ressource erhalten.
Wenn der Thread im upgradefähigen Modus den Schreibmodus verlässt, können andere Threads, die auf den Lesemodus warten, dies tun, es sei denn, es gibt Threads, die auf den Schreibmodus warten. Der Thread im upgradefähigen Modus kann unbegrenzt aktualisiert und heruntergestuft werden, solange es sich um den einzigen Thread handelt, der in die geschützte Ressource schreibt.
Wichtig
Wenn Sie zulassen, dass mehrere Threads in den Schreibmodus oder in den upgradefähigen Modus wechseln können, dürfen Sie nicht zulassen, dass ein Thread den upgradefähigen Modus monopolisiert. Andernfalls werden Threads, die versuchen, den Schreibmodus direkt einzugeben, unbegrenzt blockiert, und während sie blockiert werden, können andere Threads nicht in den Lesemodus wechseln.
Ein Thread im upgradefähigen Modus kann einen Downgrade in den Lesemodus durchführen, indem zuerst die EnterReadLock Methode aufgerufen und dann die ExitUpgradeableReadLock Methode aufgerufen wird. Dieses Downgrademuster ist für alle Sperr-Rekursionsrichtlinien zulässig, auch NoRecursion.
Nach dem Herunterstufen in den Lesemodus kann ein Thread den upgradefähigen Modus erst wieder ausführen, wenn er aus dem Lesemodus beendet wurde.
Rekursives Eingeben der Sperre
Sie können eine ReaderWriterLockSlim , die rekursive Sperreingabe unterstützt, mithilfe des Konstruktors erstellen, der ReaderWriterLockSlim(LockRecursionPolicy) die Sperrrichtlinie angibt, und angeben LockRecursionPolicy.SupportsRecursion.
Hinweis
Die Verwendung von Rekursionen wird für die neue Entwicklung nicht empfohlen, da sie unnötige Komplikationen einführt und Ihren Code anfälliger für Deadlocks macht.
Für ein ReaderWriterLockSlim Element, das rekursion zulässt, kann Folgendes über die Modi gesagt werden, die ein Thread eingeben kann:
Ein Thread im Lesemodus kann rekursiv in den Lesemodus wechseln, aber nicht in den Schreibmodus oder in den Upgrademodus wechseln. Wenn dies versucht wird, wird ein LockRecursionException Fehler ausgelöst. Wenn Sie in den Lesemodus wechseln und dann in den Schreib- oder Upgrademodus wechseln, handelt es sich um ein Muster mit einer starken Wahrscheinlichkeit von Deadlocks, sodass sie nicht zulässig ist. Wie bereits erwähnt, wird der upgradefähige Modus für Fälle bereitgestellt, in denen ein Upgrade einer Sperre erforderlich ist.
Ein Thread im upgradefähigen Modus kann in den Schreib- und/oder Lesemodus wechseln und einen der drei Modi rekursiv eingeben. Es wird jedoch versucht, Schreibmodusblöcke einzugeben, wenn andere Threads im Lesemodus vorhanden sind.
Ein Thread im Schreibmodus kann in den Lese- und/oder Upgrademodus wechseln und einen der drei Modi rekursiv eingeben.
Ein Thread, der die Sperre nicht eingegeben hat, kann in einen beliebigen Modus wechseln. Dieser Versuch kann aus denselben Gründen wie ein Versuch, eine nicht rekursive Sperre einzugeben, blockieren.
Ein Thread kann die modi beenden, die er in beliebiger Reihenfolge eingegeben hat, solange er jeden Modus genau so oft beendet, wie er in diesen Modus gelangt ist. Wenn ein Thread versucht, einen Modus zu oft zu beenden oder einen nicht eingegebenen Modus zu beenden, wird ein SynchronizationLockException Fehler ausgelöst.
Sperrstatus
Sie können es hilfreich finden, um die Sperre in Bezug auf ihre Zustände zu betrachten. Eine ReaderWriterLockSlim Kann in einem von vier Zuständen sein: nicht eingegeben, gelesen, aktualisiert und geschrieben.
Nicht eingegeben: In diesem Zustand haben keine Threads die Sperre eingegeben (oder alle Threads haben die Sperre beendet).
Lesen: In diesem Zustand haben mindestens ein Threads die Sperre für den Lesezugriff auf die geschützte Ressource eingegeben.
Hinweis
Ein Thread kann die Sperre im Lesemodus mithilfe der EnterReadLock Methoden oder TryEnterReadLock durch Herabstufen aus dem upgradefähigen Modus eingeben.
Upgrade: In diesem Zustand hat ein Thread die Sperre für den Lesezugriff mit der Option zum Upgrade auf Schreibzugriff (d. h. im upgradefähigen Modus) eingegeben, und null oder mehr Threads haben die Sperre für den Lesezugriff eingegeben. Nicht mehr als ein Thread gleichzeitig kann die Sperre mit der Option zum Upgrade eingeben. Zusätzliche Threads, die versuchen, in den Upgrademodus zu wechseln, werden blockiert.
Schreiben: In diesem Zustand hat ein Thread die Sperre für den Schreibzugriff auf die geschützte Ressource eingegeben. Dieser Thread verfügt über exklusiven Besitz der Sperre. Jeder andere Thread, der versucht, die Sperre aus irgendeinem Grund einzugeben, wird blockiert.
In der folgenden Tabelle werden die Übergänge zwischen Sperreszuständen für Sperren beschrieben, die keine Rekursion zulassen, wenn ein Thread t
die in der spalte ganz links beschriebene Aktion ausführt. Zu dem Zeitpunkt, zu dem die Aktion ausgeführt wird, t
ist kein Modus vorhanden. (Der Sonderfall, in dem t
sich der Upgrademodus befindet, wird in den Tabellennoten beschrieben.) In der obersten Zeile wird der Startzustand der Sperre beschrieben. In den Zellen wird beschrieben, was mit dem Thread geschieht, und Änderungen am Sperrzustand in Klammern angezeigt.
Übergang | Nicht eingegeben (N) | Lesen (Read, R) | Upgrade (U) | Schreiben (Write, W) |
---|---|---|---|---|
t Wechselt in den Lesemodus |
t enters (R). |
t blockiert, wenn Threads auf den Schreibmodus warten; t andernfalls wird die Zeichenfolge eingegeben. |
t blockiert, wenn Threads auf den Schreibmodus warten; t andernfalls wird die Zeichenfolge eingegeben.1 |
t Blöcke. |
t Wechselt in den upgradefähigen Modus |
t gibt (U) ein. |
t blockiert, wenn Threads auf den Schreibmodus oder den Upgrademodus warten; t andernfalls wird (U) eingegeben. |
t Blöcke. |
t Blöcke. |
t Wechselt in den Schreibmodus |
t enters (W). |
t Blöcke. |
t Blöcke.2 |
t Blöcke. |
1 Wenn t
sie im upgradefähigen Modus gestartet wird, wechselt er in den Lesemodus. Diese Aktion blockiert niemals. Der Sperrzustand ändert sich nicht. (Der Thread kann dann ein Downgrade zum Lesemodus abschließen, indem er den upgradefähigen Modus beendet.)
2 Wenn t
sie im upgradefähigen Modus gestartet werden, wird blockiert, ob Threads im Lesemodus vorhanden sind. Andernfalls wird der Schreibmodus aktualisiert. Der Sperrzustand wird in Write (W) geändert. Wenn t
Blöcke vorhanden sind, da Threads im Lesemodus vorhanden sind, wechselt er in den Schreibmodus, sobald der letzte Thread den Lesemodus beendet, auch wenn Threads warten, um in den Schreibmodus zu wechseln.
Wenn eine Zustandsänderung auftritt, weil ein Thread die Sperre verlässt, wird der nächste zu weckende Thread wie folgt ausgewählt:
- Zuerst wartet ein Thread auf den Schreibmodus und befindet sich bereits im Upgrademodus (es kann höchstens einen solchen Thread geben).
- Andernfalls wartet ein Thread, der auf den Schreibmodus wartet.
- Andernfalls wartet ein Thread, der auf den upgradefähigen Modus wartet.
- Andernfalls warten alle Threads, die auf den Lesemodus warten.
Der nachfolgende Zustand der Sperre ist immer Write (W) in den ersten beiden Fällen und Upgrade (U) im dritten Fall, unabhängig vom Zustand der Sperre, wenn der beendigungsthread die Zustandsänderung ausgelöst hat. Im letzten Fall ist der Zustand der Sperre "Upgrade (U)", wenn ein Thread nach der Zustandsänderung im Upgrademodus vorhanden ist, andernfalls "Read (R)", unabhängig vom vorherigen Zustand.
Beispiele
Das folgende Beispiel zeigt einen einfachen synchronisierten Cache, der Zeichenfolgen mit ganzzahligen Schlüsseln enthält. Eine Instanz von ReaderWriterLockSlim wird verwendet, um den Zugriff auf den Dictionary<TKey,TValue> internen Cache zu synchronisieren.
Das Beispiel enthält einfache Methoden zum Hinzufügen zum Cache, Löschen aus dem Cache und Lesen aus dem Cache. Zum Veranschaulichen von Timeouts enthält das Beispiel eine Methode, die dem Cache nur hinzugefügt wird, wenn dies innerhalb eines angegebenen Timeouts möglich ist.
Um den upgradefähigen Modus zu veranschaulichen, enthält das Beispiel eine Methode, die den einem Schlüssel zugeordneten Wert abruft und ihn mit einem neuen Wert vergleicht. Wenn der Wert unverändert ist, gibt die Methode einen Status zurück, der keine Änderung angibt. Wenn kein Wert für den Schlüssel gefunden wird, wird das Schlüssel-Wert-Paar eingefügt. Wenn sich der Wert geändert hat, wird er aktualisiert. Der upgradefähige Modus ermöglicht es dem Thread, von Lesezugriff auf Schreibzugriff nach Bedarf zu aktualisieren, ohne dass Das Risiko von Deadlocks besteht.
Das Beispiel enthält eine geschachtelte Enumeration, die die Rückgabewerte für die Methode angibt, die den upgradefähigen Modus veranschaulicht.
Im Beispiel wird der parameterlose Konstruktor zum Erstellen der Sperre verwendet, sodass rekursion nicht zulässig ist. Die ReaderWriterLockSlim Programmierung ist einfacher und weniger anfällig für Fehler, wenn die Sperre keine Rekursion zulässt.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>();
public int Count
{ get { return innerCache.Count; } }
public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
}
public void Add(int key, string value)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
}
public bool AddWithTimeout(int key, string value, int timeout)
{
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
}
public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
}
public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
}
public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
};
~SynchronizedCache()
{
if (cacheLock != null) cacheLock.Dispose();
}
}
Public Class SynchronizedCache
Private cacheLock As New ReaderWriterLockSlim()
Private innerCache As New Dictionary(Of Integer, String)
Public ReadOnly Property Count As Integer
Get
Return innerCache.Count
End Get
End Property
Public Function Read(ByVal key As Integer) As String
cacheLock.EnterReadLock()
Try
Return innerCache(key)
Finally
cacheLock.ExitReadLock()
End Try
End Function
Public Sub Add(ByVal key As Integer, ByVal value As String)
cacheLock.EnterWriteLock()
Try
innerCache.Add(key, value)
Finally
cacheLock.ExitWriteLock()
End Try
End Sub
Public Function AddWithTimeout(ByVal key As Integer, ByVal value As String, _
ByVal timeout As Integer) As Boolean
If cacheLock.TryEnterWriteLock(timeout) Then
Try
innerCache.Add(key, value)
Finally
cacheLock.ExitWriteLock()
End Try
Return True
Else
Return False
End If
End Function
Public Function AddOrUpdate(ByVal key As Integer, _
ByVal value As String) As AddOrUpdateStatus
cacheLock.EnterUpgradeableReadLock()
Try
Dim result As String = Nothing
If innerCache.TryGetValue(key, result) Then
If result = value Then
Return AddOrUpdateStatus.Unchanged
Else
cacheLock.EnterWriteLock()
Try
innerCache.Item(key) = value
Finally
cacheLock.ExitWriteLock()
End Try
Return AddOrUpdateStatus.Updated
End If
Else
cacheLock.EnterWriteLock()
Try
innerCache.Add(key, value)
Finally
cacheLock.ExitWriteLock()
End Try
Return AddOrUpdateStatus.Added
End If
Finally
cacheLock.ExitUpgradeableReadLock()
End Try
End Function
Public Sub Delete(ByVal key As Integer)
cacheLock.EnterWriteLock()
Try
innerCache.Remove(key)
Finally
cacheLock.ExitWriteLock()
End Try
End Sub
Public Enum AddOrUpdateStatus
Added
Updated
Unchanged
End Enum
Protected Overrides Sub Finalize()
If cacheLock IsNot Nothing Then cacheLock.Dispose()
End Sub
End Class
Der folgende Code verwendet dann das SynchronizedCache
Objekt, um ein Wörterbuch mit Gemüsenamen zu speichern. Es werden drei Aufgaben erstellt. Im ersten Wird die Namen von Gemüse geschrieben, die in einem Array gespeichert sind, in eine SynchronizedCache
Instanz. Der zweite und dritte Vorgang zeigt die Namen des Gemüses an, die erste in aufsteigender Reihenfolge (von niedriger Index bis hoch index), die zweite in absteigender Reihenfolge. Der letzte Vorgang sucht nach der Zeichenfolge "Gurke" und ruft die Methode auf, um die EnterUpgradeableReadLock Zeichenfolge "grüne Bohnen" zu ersetzen.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
public class Example
{
public static void Main()
{
var sc = new SynchronizedCache();
var tasks = new List<Task>();
int itemsWritten = 0;
// Execute a writer.
tasks.Add(Task.Run( () => { String[] vegetables = { "broccoli", "cauliflower",
"carrot", "sorrel", "baby turnip",
"beet", "brussel sprout",
"cabbage", "plantain",
"spinach", "grape leaves",
"lime leaves", "corn",
"radish", "cucumber",
"raddichio", "lima beans" };
for (int ctr = 1; ctr <= vegetables.Length; ctr++)
sc.Add(ctr, vegetables[ctr - 1]);
itemsWritten = vegetables.Length;
Console.WriteLine("Task {0} wrote {1} items\n",
Task.CurrentId, itemsWritten);
} ));
// Execute two readers, one to read from first to last and the second from last to first.
for (int ctr = 0; ctr <= 1; ctr++) {
bool desc = ctr == 1;
tasks.Add(Task.Run( () => { int start, last, step;
int items;
do {
String output = String.Empty;
items = sc.Count;
if (! desc) {
start = 1;
step = 1;
last = items;
}
else {
start = items;
step = -1;
last = 1;
}
for (int index = start; desc ? index >= last : index <= last; index += step)
output += String.Format("[{0}] ", sc.Read(index));
Console.WriteLine("Task {0} read {1} items: {2}\n",
Task.CurrentId, items, output);
} while (items < itemsWritten | itemsWritten == 0);
} ));
}
// Execute a red/update task.
tasks.Add(Task.Run( () => { Thread.Sleep(100);
for (int ctr = 1; ctr <= sc.Count; ctr++) {
String value = sc.Read(ctr);
if (value == "cucumber")
if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
Console.WriteLine("Changed 'cucumber' to 'green bean'");
}
} ));
// Wait for all three tasks to complete.
Task.WaitAll(tasks.ToArray());
// Display the final contents of the cache.
Console.WriteLine();
Console.WriteLine("Values in synchronized cache: ");
for (int ctr = 1; ctr <= sc.Count; ctr++)
Console.WriteLine(" {0}: {1}", ctr, sc.Read(ctr));
}
}
// The example displays the following output:
// Task 1 read 0 items:
//
// Task 3 wrote 17 items
//
//
// Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
// beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
// s] [corn] [radish] [cucumber] [raddichio] [lima beans]
//
// Task 2 read 0 items:
//
// Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
// leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
// aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
//
// Changed 'cucumber' to 'green bean'
//
// Values in synchronized cache:
// 1: broccoli
// 2: cauliflower
// 3: carrot
// 4: sorrel
// 5: baby turnip
// 6: beet
// 7: brussel sprout
// 8: cabbage
// 9: plantain
// 10: spinach
// 11: grape leaves
// 12: lime leaves
// 13: corn
// 14: radish
// 15: green bean
// 16: raddichio
// 17: lima beans
Public Module Example
Public Sub Main()
Dim sc As New SynchronizedCache()
Dim tasks As New List(Of Task)
Dim itemsWritten As Integer
' Execute a writer.
tasks.Add(Task.Run( Sub()
Dim vegetables() As String = { "broccoli", "cauliflower",
"carrot", "sorrel", "baby turnip",
"beet", "brussel sprout",
"cabbage", "plantain",
"spinach", "grape leaves",
"lime leaves", "corn",
"radish", "cucumber",
"raddichio", "lima beans" }
For ctr As Integer = 1 to vegetables.Length
sc.Add(ctr, vegetables(ctr - 1))
Next
itemsWritten = vegetables.Length
Console.WriteLine("Task {0} wrote {1} items{2}",
Task.CurrentId, itemsWritten, vbCrLf)
End Sub))
' Execute two readers, one to read from first to last and the second from last to first.
For ctr As Integer = 0 To 1
Dim flag As Integer = ctr
tasks.Add(Task.Run( Sub()
Dim start, last, stp As Integer
Dim items As Integer
Do
Dim output As String = String.Empty
items = sc.Count
If flag = 0 Then
start = 1 : stp = 1 : last = items
Else
start = items : stp = -1 : last = 1
End If
For index As Integer = start To last Step stp
output += String.Format("[{0}] ", sc.Read(index))
Next
Console.WriteLine("Task {0} read {1} items: {2}{3}",
Task.CurrentId, items, output,
vbCrLf)
Loop While items < itemsWritten Or itemsWritten = 0
End Sub))
Next
' Execute a red/update task.
tasks.Add(Task.Run( Sub()
For ctr As Integer = 1 To sc.Count
Dim value As String = sc.Read(ctr)
If value = "cucumber" Then
If sc.AddOrUpdate(ctr, "green bean") <> SynchronizedCache.AddOrUpdateStatus.Unchanged Then
Console.WriteLine("Changed 'cucumber' to 'green bean'")
End If
End If
Next
End Sub ))
' Wait for all three tasks to complete.
Task.WaitAll(tasks.ToArray())
' Display the final contents of the cache.
Console.WriteLine()
Console.WriteLine("Values in synchronized cache: ")
For ctr As Integer = 1 To sc.Count
Console.WriteLine(" {0}: {1}", ctr, sc.Read(ctr))
Next
End Sub
End Module
' The example displays output like the following:
' Task 1 read 0 items:
'
' Task 3 wrote 17 items
'
' Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
' beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
' s] [corn] [radish] [cucumber] [raddichio] [lima beans]
'
' Task 2 read 0 items:
'
' Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
' leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
' aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
'
' Changed 'cucumber' to 'green bean'
'
' Values in synchronized cache:
' 1: broccoli
' 2: cauliflower
' 3: carrot
' 4: sorrel
' 5: baby turnip
' 6: beet
' 7: brussel sprout
' 8: cabbage
' 9: plantain
' 10: spinach
' 11: grape leaves
' 12: lime leaves
' 13: corn
' 14: radish
' 15: green bean
' 16: raddichio
' 17: lima beans