Aracılığıyla paylaş


System.Threading.ReaderWriterLockSlim sınıfı

Bu makale, bu API'nin başvuru belgelerine ek açıklamalar sağlar.

Birden çok iş parçacığı tarafından okunan ve bir kerede bir iş parçacığı tarafından yazılan bir kaynağı korumak için kullanın ReaderWriterLockSlim . ReaderWriterLockSlim birden çok iş parçacığının okuma modunda olmasına izin verir, bir iş parçacığının kilidin özel sahipliğiyle yazma modunda olmasını sağlar ve okuma erişimi olan bir iş parçacığının kaynağa okuma erişiminden feragat etmek zorunda kalmadan yazma moduna yükseltebildiği yükseltilebilir okuma modunda olmasına olanak tanır.

Not

Varsayılan olarak, yeni örnekleri ReaderWriterLockSlim bayrağıyla LockRecursionPolicy.NoRecursion oluşturulur ve özyinelemelere izin vermez. Özyineleme gereksiz komplikasyonlara neden olduğundan ve kodunuzu kilitlenmelere daha açık hale getirdiği için bu varsayılan ilke tüm yeni geliştirmeler için önerilir. veya ReaderWriterLockkullanan Monitor mevcut projelerden geçişi basitleştirmek için bayrağını LockRecursionPolicy.SupportsRecursion kullanarak özyinelemeye izin veren örnekleri ReaderWriterLockSlim oluşturabilirsiniz.

İş parçacığı kilidi üç modda girebilir: okuma modu, yazma modu ve yükseltilebilir okuma modu. (Bu konunun geri kalanında ,"yükseltilebilir okuma modu" "yükseltilebilir mod" olarak adlandırılır ve "enter x mode" tümceciği daha uzun "kilit x moduna girin" ifadesi tercih edilir.)

Özyineleme ilkesinden bağımsız olarak, her zaman yalnızca bir iş parçacığı yazma modunda olabilir. bir iş parçacığı yazma modundayken, başka hiçbir iş parçacığı herhangi bir modda kilidi giremez. Herhangi bir anda yalnızca bir iş parçacığı yükseltilebilir modda olabilir. Herhangi bir sayıda iş parçacığı okuma modunda olabilir ve diğer iş parçacıkları okuma modundayken yükseltilebilir modda bir iş parçacığı olabilir.

Önemli

Bu tür arabirimini IDisposable uygular. Türünü kullanmayı bitirdiğinizde, doğrudan veya dolaylı olarak atmalısınız. Türü doğrudan atmak için yöntemini bir try/catch blok içinde çağırın.Dispose Bunu dolaylı olarak atmak için (C#'de) veya Using (Visual Basic'te) gibi using bir dil yapısı kullanın. Daha fazla bilgi için arabirim konusunun "IDisposable Uygulayan Bir Nesne Kullanma" bölümüne IDisposable bakın.

ReaderWriterLockSlim yönetilen iş parçacığı benzitesi vardır; diğer bir ifadeyle, her Thread nesnenin kilit modlarına girmek ve modlarından çıkmak için kendi yöntem çağrılarını yapması gerekir. Hiçbir iş parçacığı başka bir iş parçacığının modunu değiştiremez.

bir ReaderWriterLockSlim özyinelemeye izin vermezse, kilidi girmeye çalışan bir iş parçacığı çeşitli nedenlerle engelleyebilir:

  • Yazma moduna girmeyi bekleyen iş parçacıkları varsa veya yazma modunda tek bir iş parçacığı varsa, okuma moduna girmeye çalışan iş parçacığı blokları.

    Not

    Yazarlar kuyruğa alınırken yeni okuyucuların engellenmesi, yazarları destekleyen bir kilit eşitliği ilkesidir. Geçerli eşitlik ilkesi, en yaygın senaryolarda aktarım hızını artırmak için okuyuculara ve yazarlara eşitliği dengeler. .NET'in gelecekteki sürümleri yeni eşitlik ilkelerine neden olabilir.

  • Yükseltilebilir moda girmeye çalışan bir iş parçacığı, zaten yükseltilebilir modda bir iş parçacığı varsa, yazma moduna girmeyi bekleyen iş parçacıkları varsa veya yazma modunda tek bir iş parçacığı varsa bloklar.

  • Yazma moduna girmeye çalışan bir iş parçacığı, üç moddan birinde bir iş parçacığı varsa bloklar.

Kilitleri yükseltme ve düşürme

Yükseltilebilir mod, bir iş parçacığının genellikle korumalı kaynaktan okuduğu, ancak bir koşul karşılandığında buna yazması gerekebileceği durumlar için tasarlanmıştır. Yükseltilebilir moda giren bir ReaderWriterLockSlim iş parçacığı korumalı kaynağa okuma erişimine sahiptir ve veya TryEnterWriteLock yöntemlerini çağırarak EnterWriteLock yazma moduna yükseltebilir. Aynı anda yükseltilebilir modda yalnızca bir iş parçacığı olabileceğinden, özyineleme izin verilmediğinde yazma moduna yükseltme işlemi kilitlenemez. Bu varsayılan ilkedir.

Önemli

Özyineleme ilkesinden bağımsız olarak, başlangıçta okuma moduna giren bir iş parçacığının yükseltilebilir moda veya yazma moduna yükseltilmesine izin verilmez, çünkü bu düzen kilitlenme olasılığı güçlü bir şekilde oluşturur. Örneğin, okuma modundaki iki iş parçacığı da yazma moduna girmeye çalışırsa çıkmaza girer. Yükseltilebilir mod, bu tür kilitlenmeleri önlemek için tasarlanmıştır.

Okuma modunda başka iş parçacıkları varsa, yükselten iş parçacığı engeller. İş parçacığı engellenmiş olsa da, okuma moduna girmeye çalışan diğer iş parçacıkları engellenir. Tüm iş parçacıkları okuma modundan çıktığında engellenen yükseltilebilir iş parçacığı yazma moduna girer. Yazma moduna girmeyi bekleyen başka iş parçacıkları varsa, yükseltilebilir modda olan tek iş parçacığının kaynağa özel erişim kazanmasını engellediği için bunlar engellenmiş durumda kalır.

Yükseltilebilir modda iş parçacığı yazma modundan çıktığında, yazma moduna girmeyi bekleyen iş parçacıkları olmadığı sürece, okuma moduna girmeyi bekleyen diğer iş parçacıkları bunu yapabilir. Yükseltilebilir modda iş parçacığı, korumalı kaynağa yazan tek iş parçacığı olduğu sürece süresiz olarak yükseltilebilir ve düşürülebilir.

Önemli

Birden çok iş parçacığının yazma moduna veya yükseltilebilir moda girmesine izin verirseniz, bir iş parçacığının yükseltilebilir modu tekeline almasına izin vermemelisiniz. Aksi takdirde, doğrudan yazma moduna girmeye çalışan iş parçacıkları süresiz olarak engellenir ve engellenirken diğer iş parçacıkları okuma moduna giremez.

Yükseltilebilir modda bir iş parçacığı, önce yöntemini çağırıp ardından yöntemini çağırarak EnterReadLockExitUpgradeableReadLock okuma moduna düşürebilir. Bu sürüm düşürme düzeni, bile tüm NoRecursionkilit özyineleme ilkeleri için izin verilir.

Okuma moduna düşürme işleminden sonra, iş parçacığı okuma modundan çıkana kadar yükseltilebilir moda yeniden giremez.

Kilidi özyinelemeli olarak girin

Kilit ilkesini belirten oluşturucuyu ReaderWriterLockSlim(LockRecursionPolicy) kullanarak ve belirterek LockRecursionPolicy.SupportsRecursionözyinelemeli kilit girişini destekleyen bir ReaderWriterLockSlim oluşturabilirsiniz.

Not

Yeni geliştirme için özyineleme kullanılması önerilmez, çünkü gereksiz komplikasyonlara neden olur ve kodunuzu kilitlenmelere daha açık hale getirir.

Özyineleme sağlayan bir ReaderWriterLockSlim için, bir iş parçacığının girebileceği modlar hakkında aşağıdakiler söylenebilir:

  • Okuma modundaki bir iş parçacığı yinelemeli olarak okuma moduna girebilir, ancak yazma moduna veya yükseltilebilir moda giremez. Bunu yapmaya çalışırsa, bir LockRecursionException atılır. Okuma moduna girmek ve sonra yazma moduna veya yükseltilebilir moda girmek, kilitlenme olasılığı yüksek olan bir desendir, bu nedenle buna izin verilmez. Daha önce açıklandığı gibi, bir kilidi yükseltmenin gerekli olduğu durumlar için yükseltilebilir mod sağlanır.

  • Yükseltilebilir modda bir iş parçacığı yazma moduna ve/veya okuma moduna girebilir ve üç moddan herhangi birini özyinelemeli olarak girebilir. Ancak, okuma modunda başka iş parçacıkları varsa yazma modu blokları girme girişimi.

  • Yazma modundaki bir iş parçacığı okuma moduna ve/veya yükseltilebilir moda girebilir ve üç moddan herhangi birini özyinelemeli olarak girebilir.

  • Kilidi girmemiş bir iş parçacığı herhangi bir moda girebilir. Bu girişim, özyinelemeli olmayan bir kilit girme girişimiyle aynı nedenlerle engelleyebilir.

Bir iş parçacığı, her moddan tam olarak o moda girdiği kadar çok kez çıktığı sürece, girdiği modlardan herhangi bir sırayla çıkabilirsiniz. bir iş parçacığı bir moddan çok fazla kez çıkmaya çalışırsa veya girmediği bir moddan çıkmaya çalışırsa, bir SynchronizationLockException oluşturulur.

Durumları kilitleme

Kilidi durumları açısından düşünmek yararlı olabilir. A ReaderWriterLockSlim dört durumdan birinde olabilir: girilmedi, okundu, yükseltildi ve yazılamadı.

  • Girilmedi: Bu durumda, hiçbir iş parçacığı kilitlenmedi (veya tüm iş parçacıkları kilitte çıktı).

  • Okuma: Bu durumda, korumalı kaynağa okuma erişimi için bir veya daha fazla iş parçacığı kilit girdi.

    Not

    İş parçacığı, veya yöntemlerini kullanarak EnterReadLock veya TryEnterReadLock yükseltilebilir moddan düşürerek kilidi okuma moduna girebilir.

  • Yükseltme: Bu durumda, bir iş parçacığı yazma erişimine yükseltme seçeneğiyle (yükseltilebilir modda) okuma erişimi kilidine girdi ve sıfır veya daha fazla iş parçacığı okuma erişimi için kilit girdi. Aynı anda birden fazla iş parçacığı, yükseltme seçeneğiyle kilidi giremez; yükseltilebilir moda girmeye çalışan ek iş parçacıkları engellenir.

  • Yazma: Bu durumda, korumalı kaynağa yazma erişimi için bir iş parçacığı kilit girdi. O ipliğin kilidi özel olarak ele geçirildi. Herhangi bir nedenle kilidi girmeye çalışan diğer tüm iş parçacıkları engellenir.

Aşağıdaki tabloda, bir iş parçacığı t en soldaki sütunda açıklanan eylemi gerçekleştirdiğinde özyineleme izin verilmeyen kilitler için kilit durumları arasındaki geçişler açıklanmaktadır. Eylemi gerçekleştirmesi sırasında t modu yoktur. (Yükseltilebilir modda olduğu t özel durum tablo dipnotlarında açıklanmıştır.) Üst satır, kilidin başlangıç durumunu açıklar. Hücreler iş parçacığına ne olduğunu açıklar ve kilit durumundaki değişiklikleri parantez içinde gösterir.

Transition Girilmedi (N) Okuma (R) Yükseltme (U) Yazma (W)
t okuma moduna girer t enters (R). t iş parçacıkları yazma modunu bekliyorsa bloklar; aksi takdirde, t girer. t iş parçacıkları yazma modunu bekliyorsa bloklar; aksi takdirde, t girer.1 t Blok.
t yükseltilebilir moda girer t enters (U). t iş parçacıkları yazma modunu veya yükseltme modunu bekliyorsa bloklar; aksi takdirde ( t U) değerini girer. t Blok. t Blok.
t yazma moduna girer t enters (W). t Blok. t Blok.2 t Blok.

1 Yükseltilebilir modda başlatılırsa t okuma moduna girer. Bu eylem hiçbir zaman engellemez. Kilit durumu değişmez. (İş parçacığı daha sonra yükseltilebilir moddan çıkararak okuma moduna düşürme işlemini tamamlayabilir.)

2 Yükseltilebilir modda başlatılırsa t , okuma modunda iş parçacıkları olup olmadığını engeller. Aksi takdirde yazma moduna yükseltildi. Kilit durumu Yazma (W) olarak değişir. Okuma modunda iş parçacıkları olduğundan engellerse t , yazma moduna girmeyi bekleyen iş parçacıkları olsa bile son iş parçacığı okuma modundan çıkar çıkmaz yazma moduna girer.

bir iş parçacığı kilit çıkışından çıktığı için durum değişikliği gerçekleştiğinde, uyandırılacak sonraki iş parçacığı aşağıdaki gibi seçilir:

  • İlk olarak, yazma modunu bekleyen ve zaten yükseltilebilir modda olan bir iş parçacığı (en fazla böyle bir iş parçacığı olabilir).
  • Bu başarısız olursa, yazma modunu bekleyen bir iş parçacığı.
  • Bu başarısız olursa, yükseltilebilir modu bekleyen bir iş parçacığı.
  • Bu başarısız olursa, okuma modunu bekleyen tüm iş parçacıkları.

Kilidin sonraki durumu her zaman ilk iki durumda Yazma (W) ve üçüncü durumda, çıkış iş parçacığı durum değişikliğini tetiklediğinde kilidin durumundan bağımsız olarak Yükseltme (U) şeklindedir. Son durumda, durum değişikliğinden sonra yükseltilebilir modda bir iş parçacığı varsa kilidin durumu Yükselt (U) ve önceki durumdan bağımsız olarak Okuma (R) şeklindedir.

Örnekler

Aşağıdaki örnekte, tamsayı anahtarlı dizeleri tutan basit bir eşitlenmiş önbellek gösterilmektedir. örneği ReaderWriterLockSlim , iç önbellek görevi görecek öğesine Dictionary<TKey,TValue> erişimi eşitlemek için kullanılır.

Örnek önbelleğe eklemek, önbellekten silmek ve önbellekten okumak için basit yöntemler içerir. Zaman aşımlarını göstermek için örnek, yalnızca belirli bir zaman aşımı içinde bunu gerçekleştirebiliyorsa önbelleğe ekleyen bir yöntem içerir.

Yükseltilebilir modu göstermek için örnek, bir anahtarla ilişkili değeri alan ve yeni bir değerle karşılaştıran bir yöntem içerir. Değer değişmezse, yöntem değişiklik olmadığını belirten bir durum döndürür. Anahtar için değer bulunamazsa anahtar/değer çifti eklenir. Değer değiştiyse güncelleştirilir. Yükseltilebilir mod, iş parçacığının kilitlenme riski olmadan okuma erişiminden gerektiğinde yazma erişimine yükseltmesine olanak tanır.

Örnek, yükseltilebilir modu gösteren yöntemin dönüş değerlerini belirten iç içe sabit listesi içerir.

Örnek, kilidi oluşturmak için parametresiz oluşturucuyu kullandığından özyineleme işlemine izin verilmez. ReaderWriterLockSlim kilit özyineleme izin vermediğinde programlama daha basit ve hataya daha az eğilimli olur.

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

Aşağıdaki kod daha sonra nesnesini kullanarak sebze adları sözlüğü depolar SynchronizedCache . Üç görev oluşturur. birincisi bir dizide depolanan sebzelerin adlarını bir SynchronizedCache örneğe yazar. İkinci ve üçüncü görev, birinci artan sırada (düşük dizinden yüksek dizine), ikincisi azalan sırada olan sebzelerin adlarını görüntüler. Son görev "salatalık" dizesini arar ve bulduğunda "yeşil fasulye" dizesini değiştirmek için yöntemini çağırır EnterUpgradeableReadLock .

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