Klasse System.Threading.ReaderWriterLockSlim
In dit artikel vindt u aanvullende opmerkingen in de referentiedocumentatie voor deze API.
Gebruik ReaderWriterLockSlim dit om een resource te beveiligen die wordt gelezen door meerdere threads en naar één thread tegelijk wordt geschreven. ReaderWriterLockSlim staat toe dat meerdere threads zich in de leesmodus bevinden, dat één thread in de schrijfmodus staat met exclusief eigendom van de vergrendeling en één thread die leestoegang heeft, zich in de upgradebare leesmodus bevindt, van waaruit de thread een upgrade naar de schrijfmodus kan uitvoeren zonder de leestoegang tot de resource te hoeven weigeren.
Notitie
- ReaderWriterLockSlim is vergelijkbaar met ReaderWriterLock, maar het heeft vereenvoudigde regels voor recursie en voor het upgraden en downgraden van de vergrendelingsstatus. ReaderWriterLockSlim vermijdt veel gevallen van mogelijke impasse. Daarnaast is de prestaties ReaderWriterLockSlim aanzienlijk beter dan ReaderWriterLock. ReaderWriterLockSlim wordt aanbevolen voor alle nieuwe ontwikkeling.
- ReaderWriterLockSlim is niet thread-abort safe. U moet deze niet gebruiken in een omgeving waarin threads die toegang hebben tot de threads, kunnen worden afgebroken, zoals .NET Framework. Als u .NET Core of .NET 5+ gebruikt, moet dit prima zijn. Abort wordt niet ondersteund in .NET Core en is verouderd in .NET 5 en nieuwere versies.
Standaard worden nieuwe exemplaren gemaakt ReaderWriterLockSlim met de LockRecursionPolicy.NoRecursion vlag en zijn recursie niet toegestaan. Dit standaardbeleid wordt aanbevolen voor alle nieuwe ontwikkeling, omdat recursie onnodige complicaties introduceert en uw code gevoeliger maakt voor impasses. Als u de migratie wilt vereenvoudigen van bestaande projecten die gebruikmaken Monitor van of ReaderWriterLock, kunt u de LockRecursionPolicy.SupportsRecursion vlag gebruiken om exemplaren van ReaderWriterLockSlim die recursie te maken.
Een thread kan de vergrendeling in drie modi invoeren: leesmodus, schrijfmodus en upgradebare leesmodus. (In de rest van dit onderwerp wordt 'upgradebare leesmodus' aangeduid als 'upgradebare modus' en wordt de woordgroep 'modus invoeren x
' gebruikt in voorkeur voor de langere woordgroep 'enter the lock in x
mode'.)
Ongeacht recursiebeleid, kan slechts één thread zich op elk gewenst moment in de schrijfmodus bevinden. Wanneer een thread zich in de schrijfmodus bevindt, kan er geen andere thread de vergrendeling in elke modus invoeren. Slechts één thread kan op elk gewenst moment in de upgradebare modus zijn. Een willekeurig aantal threads kan zich in de leesmodus bevinden en er kan één thread in de upgradebare modus zijn, terwijl andere threads zich in de leesmodus bevinden.
Belangrijk
Met dit type wordt de IDisposable interface geïmplementeerd. Wanneer u klaar bent met het gebruik van het type, moet u het direct of indirect verwijderen. Als u het type rechtstreeks wilt verwijderen, roept u de Dispose methode aan in een try
/catch
blok. Als u deze indirect wilt verwijderen, gebruikt u een taalconstructie zoals using
(in C#) of Using
(in Visual Basic). Zie de sectie 'Using an Object that Implements IDisposable' (Een object gebruiken dat IDisposable implementeert) in het IDisposable interfaceonderwerp voor meer informatie.
ReaderWriterLockSlim heeft beheerde threadaffiniteit; Dat wil zeggen dat elk Thread object zijn eigen methode-aanroepen moet maken om de vergrendelingsmodi in te voeren en af te sluiten. Er kan geen thread de modus van een andere thread wijzigen.
Als een recursie ReaderWriterLockSlim niet toestaat, kan een thread die probeert de vergrendeling in te voeren om verschillende redenen blokkeren:
Een thread die probeert leesmodusblokken in te voeren als er threads zijn die wachten om de schrijfmodus in te voeren of als er één thread in de schrijfmodus staat.
Notitie
Het blokkeren van nieuwe lezers wanneer schrijvers in de wachtrij worden geplaatst, is een beleid voor vergrendelingsgetrouwheid dat schrijvers bevoordeelt. Het huidige fairness-beleid zorgt voor evenwicht tussen lezers en schrijvers, om de doorvoer in de meest voorkomende scenario's te bevorderen. Toekomstige versies van .NET kunnen nieuwe beleidsregels voor redelijkheid introduceren.
Een thread die de upgradebare modus probeert in te voeren, blokkeert als er al een thread in de upgradebare modus is, als er threads zijn die wachten op het invoeren van de schrijfmodus of als er één thread in de schrijfmodus staat.
Een thread die probeert schrijfmodusblokken in te voeren als er een thread in een van de drie modi is.
Vergrendelingen upgraden en downgraden
De upgradebare modus is bedoeld voor gevallen waarin een thread meestal wordt gelezen uit de beveiligde resource, maar er mogelijk naar moet schrijven als aan een bepaalde voorwaarde wordt voldaan. Een thread die een ReaderWriterLockSlim upgradebare modus heeft ingevoerd, heeft leestoegang tot de beveiligde resource en kan een upgrade uitvoeren naar de schrijfmodus door de EnterWriteLock of TryEnterWriteLock methoden aan te roepen. Omdat er slechts één thread tegelijk in de upgradebare modus kan zijn, kan een upgrade naar de schrijfmodus niet worden geblokkeerd wanneer recursie niet is toegestaan. Dit is het standaardbeleid.
Belangrijk
Ongeacht het recursiebeleid is een thread die in eerste instantie in de leesmodus is gegaan, niet toegestaan om een upgrade uit te voeren naar de upgradebare modus of schrijfmodus, omdat dit patroon een sterke kans op impasses creëert. Als twee threads in de leesmodus bijvoorbeeld beide proberen om de schrijfmodus in te voeren, loopt deze vast. Upgradebare modus is ontworpen om dergelijke impasses te voorkomen.
Als er andere threads in de leesmodus staan, is de thread die blokken upgradet. Terwijl de thread is geblokkeerd, worden andere threads die proberen de leesmodus in te voeren, geblokkeerd. Wanneer alle threads zijn afgesloten uit de leesmodus, wordt de geblokkeerde upgradebare thread geactiveerd in de schrijfmodus. Als er andere threads zijn die wachten op het invoeren van de schrijfmodus, blijven ze geblokkeerd, omdat de enige thread die zich in de upgradebare modus bevindt, voorkomt dat ze exclusieve toegang krijgen tot de resource.
Wanneer de thread in de upgradebare modus de schrijfmodus verlaat, kunnen andere threads die wachten op de leesmodus dit doen, tenzij er threads zijn die wachten om de schrijfmodus in te voeren. De thread in de upgradebare modus kan voor onbepaalde tijd upgraden en downgraden, zolang dit de enige thread is die naar de beveiligde resource schrijft.
Belangrijk
Als u meerdere threads toestaat om de schrijfmodus of de upgradebare modus in te voeren, moet u niet toestaan dat één thread de upgradebare modus kan verpestekken. Anders worden threads die proberen om de schrijfmodus rechtstreeks in te voeren, voor onbepaalde tijd geblokkeerd en kunnen andere threads de leesmodus niet openen.
Een thread in de upgradebare modus kan downgraden naar de leesmodus door eerst de EnterReadLock methode aan te roepen en vervolgens de methode aan te ExitUpgradeableReadLock roepen. Dit downgradepatroon is toegestaan voor alle recursiebeleid voor vergrendelingen, zelfs NoRecursion.
Na het downgraden naar de leesmodus kan een thread de upgradebare modus pas opnieuw uitvoeren nadat deze is afgesloten vanuit de leesmodus.
Voer recursief de vergrendeling in
U kunt een ReaderWriterLockSlim maken die recursieve vergrendelingsvermelding ondersteunt met behulp van de ReaderWriterLockSlim(LockRecursionPolicy) constructor waarmee het vergrendelingsbeleid wordt opgegeven en opgeven LockRecursionPolicy.SupportsRecursion.
Notitie
Het gebruik van recursie wordt niet aanbevolen voor nieuwe ontwikkeling, omdat het onnodige complicaties introduceert en uw code gevoeliger maakt voor impasses.
Voor een ReaderWriterLockSlim die recursie toestaat, kan het volgende worden gezegd over de modi die een thread kan invoeren:
Een thread in de leesmodus kan recursief de leesmodus inschakelen, maar kan de schrijfmodus of de upgradebare modus niet activeren. Als het dit probeert te doen, wordt er een LockRecursionException gegooid. Het invoeren van de leesmodus en het invoeren van de schrijfmodus of upgradebare modus is een patroon met een sterke kans op impasses, dus dit is niet toegestaan. Zoals eerder besproken, wordt de upgradebare modus geboden voor gevallen waarin het nodig is om een vergrendeling bij te werken.
Een thread in de upgradebare modus kan de schrijfmodus en/of leesmodus invoeren en een van de drie modi recursief invoeren. Een poging om schrijfmodusblokken in te voeren als er andere threads in de leesmodus staan.
Een thread in de schrijfmodus kan de leesmodus en/of de upgradebare modus inschakelen en kan recursief een van de drie modi invoeren.
Een thread die niet in de vergrendeling is gegaan, kan elke modus inschakelen. Deze poging kan om dezelfde redenen blokkeren als een poging om een niet-recursieve vergrendeling in te voeren.
Een thread kan de modi afsluiten die in elke willekeurige volgorde zijn ingevoerd, zolang elke modus precies zo vaak wordt afgesloten als deze in die modus is gegaan. Als een thread een modus te vaak probeert af te sluiten of een modus wilt afsluiten die niet is ingevoerd, wordt er een SynchronizationLockException gegenereerd.
Vergrendelingsstatussen
Misschien vindt u het handig om de vergrendeling te beschouwen in termen van de statussen. Een ReaderWriterLockSlim kan een van de vier statussen hebben: niet ingevoerd, lezen, upgraden en schrijven.
Niet ingevoerd: In deze status zijn er geen threads binnengekomen (of alle threads hebben de vergrendeling afgesloten).
Lees: In deze status hebben een of meer threads de vergrendeling ingevoerd voor leestoegang tot de beveiligde resource.
Notitie
Een thread kan de vergrendeling in de leesmodus invoeren met behulp van de EnterReadLock of TryEnterReadLock methoden of door een downgrade uit de upgradebare modus uit te voeren.
Upgrade: In deze status heeft één thread de vergrendeling voor leestoegang ingevoerd met de optie om te upgraden naar schrijftoegang (in de upgradebare modus) en nul of meer threads hebben de vergrendeling voor leestoegang ingevoerd. Niet meer dan één thread tegelijk kan de vergrendeling invoeren met de optie om een upgrade uit te voeren; extra threads die proberen de upgradebare modus in te voeren, worden geblokkeerd.
Schrijven: In deze status heeft één thread de vergrendeling ingevoerd voor schrijftoegang tot de beveiligde resource. Die draad heeft exclusief bezit van de slot. Alle andere threads die om welke reden dan ook proberen de vergrendeling in te voeren, worden geblokkeerd.
In de volgende tabel worden de overgangen tussen vergrendelingsstatussen beschreven, voor vergrendelingen die geen recursie toestaan, wanneer een thread t
de actie onderneemt die in de meest linkse kolom wordt beschreven. Op het moment dat de actie wordt uitgevoerd, t
heeft deze geen modus. (Het speciale geval waarin t
de upgradebare modus wordt weergegeven, wordt beschreven in de voetnoten van de tabel.) In de bovenste rij wordt de beginstatus van de vergrendeling beschreven. In de cellen wordt beschreven wat er met de thread gebeurt en worden wijzigingen in de vergrendelingsstatus tussen haakjes weergegeven.
Transition | Niet ingevoerd (N) | Lezen (L) | Upgrade (U) | Schrijven (S) |
---|---|---|---|---|
t in de leesmodus |
t voert (R) in. |
t wordt geblokkeerd als threads wachten op de schrijfmodus; anders voert t u dit in. |
t wordt geblokkeerd als threads wachten op de schrijfmodus; anders voert t u dit in.1 |
t Blokken. |
t voert de upgradebare modus in |
t voert (U) in. |
t wordt geblokkeerd als threads wachten op de schrijfmodus of upgrademodus; t anders voert u (U) in. |
t Blokken. |
t Blokken. |
t voert de schrijfmodus in |
t voert (W) in. |
t Blokken. |
t Blokken.2 |
t Blokken. |
1 Als t
deze wordt gestart in de upgradebare modus, wordt de leesmodus geactiveerd. Deze actie blokkeert nooit. De vergrendelingsstatus verandert niet. (De thread kan vervolgens een downgrade naar de leesmodus voltooien door de upgradebare modus af te sluiten.)
2 Als t
wordt gestart in de upgradebare modus, wordt het geblokkeerd als er threads in de leesmodus staan. Anders wordt er een upgrade uitgevoerd naar de schrijfmodus. De vergrendelingsstatus verandert in Schrijven (W). Als t
er blokken zijn omdat er threads in de leesmodus staan, wordt de schrijfmodus geactiveerd zodra de laatste thread de leesmodus verlaat, zelfs als er threads wachten om de schrijfmodus in te schakelen.
Wanneer een statuswijziging optreedt omdat een thread de vergrendeling verlaat, wordt de volgende thread geselecteerd die moet worden wakker:
- Eerst een thread die wacht op de schrijfmodus en zich al in de upgradebare modus bevindt (er kan maximaal één dergelijke thread zijn).
- Dit mislukt, een thread die wacht op de schrijfmodus.
- Mislukt dat, een thread die wacht op de upgradebare modus.
- Als dat niet lukt, wachten alle threads op de leesmodus.
De volgende status van de vergrendeling is altijd Schrijven (W) in de eerste twee gevallen en Upgrade (U) in het derde geval, ongeacht de status van de vergrendeling wanneer de afsluitende thread de statuswijziging heeft geactiveerd. In het laatste geval is de status van de vergrendeling Upgrade (U) als er een thread in de upgradebare modus staat na de statuswijziging en lezen (R), ongeacht de vorige status.
Voorbeelden
In het volgende voorbeeld ziet u een eenvoudige gesynchroniseerde cache met tekenreeksen met gehele getallen. Een exemplaar van ReaderWriterLockSlim wordt gebruikt om de toegang te synchroniseren met de Dictionary<TKey,TValue> toegang die fungeert als de binnenste cache.
Het voorbeeld bevat eenvoudige methoden om aan de cache toe te voegen, uit de cache te verwijderen en uit de cache te lezen. Om time-outs te demonstreren, bevat het voorbeeld een methode die alleen aan de cache wordt toegevoegd als dit binnen een opgegeven time-out kan.
Ter illustratie van de upgradebare modus bevat het voorbeeld een methode waarmee de waarde wordt opgehaald die is gekoppeld aan een sleutel en deze vergelijkt met een nieuwe waarde. Als de waarde ongewijzigd is, retourneert de methode een status die aangeeft dat er geen wijziging is. Als er geen waarde voor de sleutel wordt gevonden, wordt het sleutel-/waardepaar ingevoegd. Als de waarde is gewijzigd, wordt deze bijgewerkt. Met de upgradebare modus kan de thread naar behoefte upgraden van leestoegang tot schrijftoegang, zonder dat er een impasse is.
Het voorbeeld bevat een geneste opsomming waarmee de retourwaarden worden opgegeven voor de methode die de upgradebare modus demonstreert.
In het voorbeeld wordt de parameterloze constructor gebruikt om de vergrendeling te maken, dus recursie is niet toegestaan. Programmeren van de ReaderWriterLockSlim is eenvoudiger en minder gevoelig voor fouten wanneer de vergrendeling geen recursie toestaat.
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
De volgende code gebruikt vervolgens het SynchronizedCache
object om een woordenlijst met groentenamen op te slaan. Er worden drie taken gemaakt. De eerste schrijft de namen van groenten die zijn opgeslagen in een matrix naar een SynchronizedCache
instantie. In de tweede en derde taak worden de namen van de groenten weergegeven, de eerste in oplopende volgorde (van lage index tot hoge index), de tweede in aflopende volgorde. De laatste taak zoekt naar de tekenreeks 'komkommer' en roept de methode aan om de EnterUpgradeableReadLock tekenreeks 'groene bean' te vervangen.
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