System.Threading.ReaderWriterLockSlim 클래스
이 문서에서는 이 API에 대한 참조 설명서에 대한 추가 설명서를 제공합니다.
여러 스레드에서 읽고 한 번에 하나의 스레드에 의해 기록되는 리소스를 보호하는 데 사용합니다 ReaderWriterLockSlim . ReaderWriterLockSlim 를 사용하면 여러 스레드가 읽기 모드에 있고, 한 스레드가 잠금의 단독 소유권이 있는 쓰기 모드에 있을 수 있으며, 읽기 액세스 권한이 있는 하나의 스레드가 업그레이드 가능한 읽기 모드로 전환할 수 있습니다. 이 스레드는 리소스에 대한 읽기 액세스를 포기하지 않고도 쓰기 모드로 업그레이드할 수 있습니다.
참고 항목
- ReaderWriterLockSlim은 ReaderWriterLock과 비슷하지만 재귀 및 잠금 상태 업그레이드/다운그레이드에 대한 간소화된 규칙을 포함합니다. ReaderWriterLockSlim은 교착 상태가 발생할 수 있는 많은 경우를 방지합니다. 또한 ReaderWriterLockSlim의 성능이 ReaderWriterLock보다 훨씬 더 놓습니다. ReaderWriterLockSlim은 모든 새 개발에 권장됩니다.
- ReaderWriterLockSlim 는 스레드 중단이 안전하지 않습니다. .NET Framework와 같이 액세스하는 스레드가 중단될 수 있는 환경에서는 사용하지 않아야 합니다. .NET Core 또는 .NET 5+를 사용하는 경우 괜찮을 것입니다. Abort 는 .NET Core 에서 지원되지 않으며 .NET 5 이상 버전에서는 사용되지 않습니다.
기본적으로 새 인스턴스는 ReaderWriterLockSlim 플래그를 사용하여 LockRecursionPolicy.NoRecursion 생성되며 재귀를 허용하지 않습니다. 재귀로 인해 불필요한 복잡성이 발생하고 코드가 교착 상태가 발생하기 쉽기 때문에 이 기본 정책은 모든 새 개발에 권장됩니다. 사용하거나 ReaderWriterLock사용하는 Monitor 기존 프로젝트에서 마이그레이션을 간소화하려면 플래그를 LockRecursionPolicy.SupportsRecursion 사용하여 재귀를 허용하는 인스턴스를 ReaderWriterLockSlim 만들 수 있습니다.
스레드는 읽기 모드, 쓰기 모드 및 업그레이드 가능한 읽기 모드의 세 가지 모드로 잠금을 입력할 수 있습니다. (이 항목의 나머지 단계에서는 "업그레이드 가능한 읽기 모드"를 "업그레이드 가능한 모드"라고 하며, "enter x
mode"라는 구는 "모드에서 잠금 입력"이라는 긴 구를 기본 설정으로 x
사용합니다.)
재귀 정책에 관계없이 언제든지 하나의 스레드만 쓰기 모드에 있을 수 있습니다. 스레드가 쓰기 모드인 경우 다른 스레드는 어떤 모드에서도 잠금을 입력할 수 없습니다. 한 번에 하나의 스레드만 업그레이드 가능한 모드에 있을 수 있습니다. 임의의 스레드 수는 읽기 모드일 수 있으며, 다른 스레드가 읽기 모드에 있는 동안 업그레이드 가능한 모드에 스레드가 하나 있을 수 있습니다.
Important
이 형식이 구현 하는 IDisposable 인터페이스입니다. 형식을 사용 하 여 마쳤으면 직접 또는 간접적으로의 삭제 해야 있습니다. 직접 형식의 dispose 호출 해당 Dispose 의 메서드를 try
/catch
블록입니다. 삭제 하지 직접, 언어 구문 같은 사용 using
(C#에서) 또는 Using
(Visual Basic에서는). 자세한 내용은 "를 사용 하는 개체는 구현 IDisposable" 섹션을 참조 하세요.를 IDisposable 인터페이스 항목입니다.
ReaderWriterLockSlim 에는 관리되는 스레드 선호도가 있습니다. 즉, 각 Thread 개체는 잠금 모드를 입력하고 종료하기 위해 자체 메서드 호출을 수행해야 합니다. 스레드는 다른 스레드의 모드를 변경할 수 없습니다.
재귀를 ReaderWriterLockSlim 허용하지 않는 경우 잠금을 입력하려는 스레드는 다음과 같은 여러 가지 이유로 차단할 수 있습니다.
쓰기 모드로 전환하기 위해 대기하는 스레드가 있거나 쓰기 모드에 단일 스레드가 있는 경우 읽기 모드 블록으로 들어가려고 하는 스레드입니다.
참고 항목
작성자가 대기 중일 때 새 판독기를 차단하는 것은 작성자를 선호하는 잠금 공정성 정책입니다. 현재의 공정성 정책은 가장 일반적인 시나리오에서 처리량을 촉진하기 위해 독자와 작가에게 공정성을 분산합니다. 이후 버전의 .NET에서는 새로운 공정성 정책을 도입할 수 있습니다.
업그레이드 가능한 모드에 스레드가 이미 있거나, 쓰기 모드로 전환하기 위해 대기 중인 스레드가 있거나, 쓰기 모드에 단일 스레드가 있는 경우 업그레이드 가능한 모드로 전환하려는 스레드가 차단됩니다.
세 가지 모드 중 한 가지 모드에 스레드가 있는 경우 쓰기 모드 블록으로 들어가려고 하는 스레드입니다.
잠금 업그레이드 및 다운그레이드
업그레이드 가능 모드는 스레드가 일반적으로 보호된 리소스에서 읽지만 일부 조건이 충족되는 경우 이를 작성해야 하는 경우를 위한 것입니다. 업그레이드 가능한 모드로 ReaderWriterLockSlim 전환된 스레드는 보호된 리소스에 대한 읽기 액세스 권한을 가지며, 또는 TryEnterWriteLock 메서드를 호출 EnterWriteLock 하여 쓰기 모드로 업그레이드할 수 있습니다. 업그레이드 가능한 모드에는 한 번에 하나의 스레드만 있을 수 있으므로 재귀가 허용되지 않을 때 쓰기 모드로 업그레이드하면 교착 상태가 될 수 없습니다. 이것이 기본 정책입니다.
Important
재귀 정책에 관계없이 처음에 읽기 모드로 전환된 스레드는 업그레이드 가능한 모드 또는 쓰기 모드로 업그레이드할 수 없습니다. 이 패턴은 교착 상태의 강력한 확률을 생성하기 때문입니다. 예를 들어 읽기 모드의 두 스레드가 모두 쓰기 모드로 전환하려고 하면 교착 상태가 발생합니다. 업그레이드 가능한 모드는 이러한 교착 상태를 방지하도록 설계되었습니다.
읽기 모드에 다른 스레드가 있는 경우 블록을 업그레이드하는 스레드입니다. 스레드가 차단되는 동안 읽기 모드로 전환하려는 다른 스레드는 차단됩니다. 모든 스레드가 읽기 모드에서 종료되면 차단된 업그레이드 가능한 스레드가 쓰기 모드로 전환됩니다. 다른 스레드가 쓰기 모드로 전환되기를 기다리는 경우 업그레이드 가능한 모드에 있는 단일 스레드가 리소스에 대한 배타적인 액세스 권한을 얻을 수 없으므로 다시 기본 차단됩니다.
업그레이드 가능한 모드의 스레드가 쓰기 모드를 종료하면 쓰기 모드로 전환하기 위해 대기 중인 스레드가 없는 한 읽기 모드로 전환되기를 기다리는 다른 스레드가 이 작업을 수행할 수 있습니다. 업그레이드 가능한 모드의 스레드는 보호된 리소스에 쓰는 유일한 스레드인 한 무기한 업그레이드 및 다운그레이드할 수 있습니다.
Important
여러 스레드가 쓰기 모드 또는 업그레이드 가능 모드로 전환되도록 허용하는 경우 한 스레드가 업그레이드 가능 모드를 독점하도록 허용해서는 안 됩니다. 그렇지 않으면 직접 쓰기 모드로 전환하려는 스레드가 무기한 차단되고 차단되는 동안 다른 스레드는 읽기 모드로 전환할 수 없습니다.
업그레이드 가능한 모드의 스레드는 먼저 메서드를 호출한 다음 메서드를 호출 EnterReadLock 하여 읽기 모드로 다운그레이드할 ExitUpgradeableReadLock 수 있습니다. 이 다운그레이드 패턴은 모든 잠금 재귀 정책에 NoRecursion도 허용됩니다.
읽기 모드로 다운그레이드한 후에는 스레드가 읽기 모드에서 종료될 때까지 업그레이드 가능한 모드를 다시 입력할 수 없습니다.
잠금을 재귀적으로 입력합니다.
잠금 정책을 지정하는 생성자를 사용하고 ReaderWriterLockSlim(LockRecursionPolicy) 지정하여 재귀 잠금 항목을 지원하는 항목을 만들 ReaderWriterLockSlim 수 있습니다LockRecursionPolicy.SupportsRecursion.
참고 항목
불필요한 복잡성을 유발하고 코드가 교착 상태에 빠지기 쉽기 때문에 재귀를 사용하는 것은 새로운 개발에 권장되지 않습니다.
ReaderWriterLockSlim 재귀를 허용하는 경우 스레드가 입력할 수 있는 모드에 대해 다음을 확인할 수 있습니다.
읽기 모드의 스레드는 재귀적으로 읽기 모드로 전환할 수 있지만 쓰기 모드 또는 업그레이드 가능한 모드로 전환할 수는 없습니다. 이 작업을 수행하려고 하면 throw LockRecursionException 됩니다. 읽기 모드를 입력한 다음 쓰기 모드 또는 업그레이드 가능 모드를 입력하는 것은 교착 상태의 가능성이 높은 패턴이므로 허용되지 않습니다. 앞에서 설명한 대로 잠금을 업그레이드해야 하는 경우 업그레이드 가능한 모드가 제공됩니다.
업그레이드 가능한 모드의 스레드는 쓰기 모드 및/또는 읽기 모드로 전환할 수 있으며 세 가지 모드 중 한 가지 모드를 재귀적으로 입력할 수 있습니다. 그러나 읽기 모드에 다른 스레드가 있는 경우 쓰기 모드로 전환하려고 하면 블록이 차단됩니다.
쓰기 모드의 스레드는 읽기 모드 및/또는 업그레이드 가능 모드로 전환할 수 있으며, 세 가지 모드 중 한 가지 모드를 재귀적으로 입력할 수 있습니다.
잠금에 들어가지 않은 스레드는 모든 모드로 전환할 수 있습니다. 이 시도는 재귀적이지 않은 잠금을 입력하려는 시도와 동일한 이유로 차단할 수 있습니다.
스레드는 해당 모드를 입력한 횟수만큼 정확하게 각 모드를 종료하는 한 순서에 따라 입력한 모드를 종료할 수 있습니다. 스레드가 모드를 너무 여러 번 종료하거나 입력되지 않은 모드를 종료하려고 하면 throw SynchronizationLockException 됩니다.
잠금 상태
상태 측면에서 잠금을 생각하는 것이 유용할 수 있습니다. A ReaderWriterLockSlim 는 입력, 읽기, 업그레이드 및 쓰기가 아닌 네 가지 상태 중 하나일 수 있습니다.
입력되지 않음: 이 상태에서는 스레드가 잠금에 들어가지 않았거나 모든 스레드가 잠금을 종료했습니다.
읽기: 이 상태에서 하나 이상의 스레드가 보호된 리소스에 대한 읽기 액세스에 대한 잠금을 입력했습니다.
참고 항목
스레드는 또는 메서드를 사용 EnterReadLock 하거나 TryEnterReadLock 업그레이드 가능한 모드에서 다운그레이드하여 읽기 모드로 잠금을 입력할 수 있습니다.
업그레이드: 이 상태에서는 한 스레드가 쓰기 액세스로 업그레이드하는 옵션(즉, 업그레이드 가능 모드)을 사용하여 읽기 액세스 잠금에 들어갔고, 0개 이상의 스레드가 읽기 액세스 잠금에 들어갔습니다. 한 번에 두 개 이상의 스레드가 업그레이드 옵션으로 잠금에 들어갈 수 없습니다. 업그레이드 가능한 모드로 전환하려는 추가 스레드가 차단됩니다.
쓰기: 이 상태에서 한 스레드가 보호된 리소스에 대한 쓰기 액세스에 대한 잠금을 입력했습니다. 해당 스레드는 잠금을 독점적으로 소유합니다. 어떤 이유로든 잠금을 입력하려고 시도하는 다른 스레드는 차단됩니다.
다음 표에서는 스레드 t
가 맨 왼쪽 열에 설명된 작업을 수행할 때 재귀를 허용하지 않는 잠금의 잠금 상태 간 전환을 설명합니다. 작업을 t
수행할 때 모드가 없습니다. (업그레이드 가능 모드인 특수한 경우 t
는 표 각주에 설명되어 있습니다.) 맨 위 행은 잠금의 시작 상태를 설명합니다. 셀은 스레드에 발생하는 작업을 설명하고 잠금 상태의 변경 내용을 괄호로 표시합니다.
전환 | 입력 안 됨(N) | 읽기(R) | 업그레이드(U) | 쓰기(W) |
---|---|---|---|---|
t 읽기 모드로 전환 |
t enters(R). |
t 스레드가 쓰기 모드를 기다리는 경우 를 차단합니다. 그렇지 않으면 t 입력합니다. |
t 스레드가 쓰기 모드를 기다리는 경우 를 차단합니다. 그렇지 않으면 t 입력합니다.1 |
t 블록. |
t 업그레이드 가능 모드로 전환 |
t enters(U)입니다. |
t 스레드가 쓰기 모드 또는 업그레이드 모드를 기다리는 경우 을 차단합니다. 그렇지 않으면 t (U)를 입력합니다. |
t 블록. |
t 블록. |
t 쓰기 모드로 전환 |
t enters(W). |
t 블록. |
t 블록.2 |
t 블록. |
1t
업그레이드 가능 모드에서 시작하면 읽기 모드로 전환됩니다. 이 작업은 차단되지 않습니다. 잠금 상태는 변경되지 않습니다. 그러면 스레드가 업그레이드 가능한 모드를 종료하여 읽기 모드로 다운그레이드를 완료할 수 있습니다.
2 업그레이드 가능한 모드에서 시작하는 경우 t
읽기 모드에 스레드가 있는 경우 차단됩니다. 그렇지 않으면 쓰기 모드로 업그레이드됩니다. 잠금 상태가 쓰기(W)로 변경됩니다. 읽기 모드에 스레드가 있기 때문에 차단되는 경우 t
쓰기 모드로 전환되기 위해 대기 중인 스레드가 있더라도 마지막 스레드가 읽기 모드를 종료하는 즉시 쓰기 모드로 전환됩니다.
스레드가 잠금을 종료하여 상태가 변경되면 다음 스레드가 다음과 같이 선택됩니다.
- 첫째, 쓰기 모드를 기다리고 있으며 업그레이드 가능한 모드에 있는 스레드입니다(이러한 스레드는 최대 하나일 수 있습니다).
- 이 오류는 쓰기 모드를 기다리는 스레드입니다.
- 실패합니다. 업그레이드 가능한 모드를 기다리는 스레드입니다.
- 실패하면 읽기 모드를 기다리는 모든 스레드가 발생합니다.
잠금의 후속 상태는 종료 스레드가 상태 변경을 트리거한 경우 잠금 상태에 관계없이 처음 두 경우의 경우 항상 쓰기(W)이고 세 번째 경우에는 업그레이드(U)입니다. 마지막 경우 상태 변경 후 업그레이드 가능한 모드에 스레드가 있는 경우 잠금 상태는 업그레이드(U)이고, 그렇지 않으면 이전 상태에 관계없이 읽기(R)입니다.
예제
다음 예제에서는 정수 키가 있는 문자열을 보유하는 간단한 동기화 캐시를 보여 줍니다. 인스턴스 ReaderWriterLockSlim 는 내부 캐시 역할을 하는 액세스 권한을 동기화하는 Dictionary<TKey,TValue> 데 사용됩니다.
이 예제에는 캐시에 추가하고, 캐시에서 삭제하고, 캐시에서 읽는 간단한 메서드가 포함되어 있습니다. 시간 초과를 보여주기 위해 이 예제에는 지정된 제한 시간 내에 캐시를 추가할 수 있는 경우에만 캐시에 추가하는 메서드가 포함됩니다.
업그레이드 가능한 모드를 보여 주는 예제에는 키와 연결된 값을 검색하고 새 값과 비교하는 메서드가 포함되어 있습니다. 값이 변경되지 않은 경우 메서드는 변경 사항을 나타내는 상태 반환합니다. 키에 대한 값을 찾을 수 없으면 키/값 쌍이 삽입됩니다. 값이 변경되면 업데이트됩니다. 업그레이드 가능 모드를 사용하면 스레드가 교착 상태의 위험 없이 필요에 따라 읽기 액세스에서 쓰기 액세스로 업그레이드할 수 있습니다.
이 예제에는 업그레이드 가능한 모드를 보여 주는 메서드의 반환 값을 지정하는 중첩된 열거형이 포함되어 있습니다.
이 예제에서는 매개 변수가 없는 생성자를 사용하여 잠금을 만들므로 재귀가 허용되지 않습니다. 잠금에서 ReaderWriterLockSlim 재귀를 허용하지 않는 경우 프로그래밍이 더 간단하고 오류가 발생하기 쉽습니다.
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
다음 코드는 개체를 SynchronizedCache
사용하여 야채 이름의 사전을 저장합니다. 세 가지 작업을 만듭니다. 첫 번째는 배열에 저장된 채소의 이름을 인스턴스에 SynchronizedCache
씁니다. 두 번째 및 세 번째 작업은 채소의 이름을 표시하고, 첫 번째 작업은 오름차순(낮은 인덱스에서 높은 인덱스로), 두 번째는 내림차순으로 표시합니다. 마지막 작업은 문자열 "오이"를 검색하고 이를 찾으면 메서드를 호출 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
.NET