Resumo : neste tutorial, você aprenderá como usar a ReaderWriterLockSlim
classe C# para controlar o acesso a um recurso compartilhado.
Introdução à classe C# ReaderWriterLockSlim
Às vezes, você tem vários threads que leem um recurso compartilhado, mas apenas alguns threads que gravam nele. Nesse caso, o uso da lock
instrução pode diminuir o desempenho do aplicativo.
O motivo é que a lock
instrução pode impedir que vários threads leiam o recurso compartilhado, mesmo que esses threads não precisem de acesso exclusivo.
A ReaderWriterLockSlim
classe permite que vários threads leiam o recurso compartilhado simultaneamente, mas um thread grave nele por vez. Portanto, o ReaderWriterLockSlim
pode ajudar a melhorar o desempenho em comparação com a lock
afirmação.
O ReaderWriterLockSlim
possui dois tipos de fechaduras:
- Bloqueio de leitura
- Bloqueio de gravador
O ReaderWriterLockSlim
permite que vários threads adquiram o bloqueio de leitura ao mesmo tempo, desde que nenhum thread tenha adquirido o bloqueio de gravação.
Além disso, ele permite que apenas um único thread adquira o bloqueio de gravação por vez e impede que outros threads tentem adquirir o bloqueio de leitura ou gravação.
Na prática, ReaderWriterLockSlim
é útil para cenários em que seus aplicativos possuem vários leitores e menos gravadores.
Observe que isso ReaderWriterLockSlim
incorre em alguma sobrecarga. Portanto, você deve utilizá-lo quando os benefícios do acesso simultâneo forem superiores ao custo de aquisição e liberação do bloqueio.
Exemplo C# ReaderWriterLockSlim
O programa a seguir demonstra como usar o ReaderWriterLockSlim
objeto para controlar o acesso simultâneo a uma variável compartilhada com cinco leitores e dois gravadores:
using static System.Console;
int counter = 0;
ReaderWriterLockSlim _lock = new();
void Read()
{
while (true)
{
try
{
_lock.EnterReadLock();
WriteLine($"R: Thread {Thread.CurrentThread.ManagedThreadId} is reading: {counter}");
}
finally
{
_lock.ExitReadLock();
}
Thread.Sleep(500);
}
}
void Write()
{
while (true)
{
try
{
_lock.EnterWriteLock();
WriteLine($"W: Thread {Thread.CurrentThread.ManagedThreadId} is writing: {counter++}");
}
finally
{
_lock.ExitWriteLock();
}
Thread.Sleep(2000);
}
}
// create 5 reader threads
for (int i = 0; i < 5; i++)
{
new Thread(() => Read()).Start();
}
// create 2 writer threads
for (int i = 0; i < 2; i++)
{
new Thread(() => Write()).Start();
}
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, declare uma variável inteira counter
:
int counter = 0;
Linguagem de código: C# ( cs )
Segundo, crie um novo ReaderWriterLockSlim
objeto chamado _lock
:
ReaderWriterLockSlim _lock = new();
Linguagem de código: C# ( cs )
Terceiro, defina o Reader()
método que adquire um bloqueio de leitura no _lock
objeto chamando o EnterReadLock()
método no try
bloco:
void Read()
{
while (true)
{
try
{
_lock.EnterReadLock();
WriteLine($"R: Thread {Thread.CurrentThread.ManagedThreadId} is reading: {counter}");
}
finally
{
_lock.ExitReadLock();
}
Thread.Sleep(500);
}
}
Linguagem de código: C# ( cs )
O Read()
método exibe o valor da counter
variável.
Além disso, ele libera o bloqueio de leitura do _lock
objeto no finally
bloco chamando o ExitReadLock()
método.
O Read()
método atrasa 500 milissegundos, o que equivale a meio segundo.
Quarto, defina o Write
método que aumenta a variável compartilhada counter
:
void Write()
{
while (true)
{
try
{
_lock.EnterWriteLock();
WriteLine($"W: Thread {Thread.CurrentThread.ManagedThreadId} is writing: {counter++}");
}
finally
{
_lock.ExitWriteLock();
}
Thread.Sleep(2000);
}
}
Linguagem de código: C# ( cs )
O Write
método adquire um bloqueio de gravação no _lock
objeto do try
bloco chamando o EnterWriteLock()
método.
Depois disso, aumenta em counter
um e exibe o counter
valor no console. O Write
método libera o bloqueio de gravação no finally
bloco chamando o ExitWriteLock()
método.
O Write
método tem um atraso de 2.000 milissegundos, ou seja, 2 segundos.
Quinto, crie dois threads de leitura e dois threads de gravação. Os threads leitores executam o Read
método enquanto os threads gravadores executam o Write
método.
// create 5 reader threads
for (int i = 0; i < 5; i++)
{
new Thread(() => Read()).Start();
}
// create 2 writer threads
for (int i = 0; i < 2; i++)
{
new Thread(() => Write()).Start();
}
Linguagem de código: C# ( cs )
Aqui está um exemplo de saída do programa:
R: Thread 9 is reading: 0
R: Thread 8 is reading: 0
R: Thread 10 is reading: 0
R: Thread 11 is reading: 0
W: Thread 13 is writing: 0
W: Thread 12 is writing: 1
R: Thread 7 is reading: 2
R: Thread 10 is reading: 2
R: Thread 11 is reading: 2
R: Thread 7 is reading: 2
R: Thread 9 is reading: 2
R: Thread 8 is reading: 2
R: Thread 7 is reading: 2
R: Thread 11 is reading: 2
R: Thread 8 is reading: 2
R: Thread 9 is reading: 2
R: Thread 10 is reading: 2
R: Thread 9 is reading: 2
R: Thread 8 is reading: 2
R: Thread 11 is reading: 2
R: Thread 10 is reading: 2
R: Thread 7 is reading: 2
W: Thread 13 is writing: 2
W: Thread 12 is writing: 3
R: Thread 8 is reading: 4
R: Thread 9 is reading: 4
R: Thread 9 is reading: 4
R: Thread 10 is reading: 4
R: Thread 7 is reading: 4
R: Thread 11 is reading: 4
R: Thread 8 is reading: 4
R: Thread 8 is reading: 4
R: Thread 11 is reading: 4
R: Thread 7 is reading: 4
R: Thread 10 is reading: 4
R: Thread 9 is reading: 4
...
Linguagem de código: C# ( cs )
Para encerrar o programa, você precisa fechá-lo.
Resumo
- Use C#
ReaderWriterLockSlim
para permitir que vários threads leiam o recurso compartilhado simultaneamente, mas um thread grave nele por vez.