O conceito de locks de leitura-escrita é simples, permitindo que múltiplas threads adquiram locks de leitura ao mesmo tempo, mas apenas uma thread pode obter bloqueios de escrita ao mesmo tempo, por isso também é chamada de locks exclusivos compartilhados. Em C#, recomenda-se usar a classe ReaderWriterLockSlim para completar a função de bloqueio de leitura/escrita. Em alguns casos, o número de leituras de um objeto é muito maior do que o número de modificações, e se ele for simplesmente travado por travamento, isso afetará a eficiência da leitura. Se um bloqueio de leitura-escrita for usado, múltiplas threads podem ler o objeto ao mesmo tempo, e ele só será bloqueado quando o objeto estiver ocupado pelo bloqueio de escrita. Simplificando, quando uma thread entra no modo de leitura, outras threads ainda podem entrar no modo de leitura, assumindo que uma thread queira entrar no modo de escrita nesse momento, então ela precisa ser bloqueada. até que o modo leitura saia. Da mesma forma, se uma thread entra em modo de escrita, as outras threads serão bloqueadas, quer queiram escrever ou ler. Existem 2 maneiras de entrar no modo de escrita/leitura: O EnterReadLock tenta entrar no estado de bloqueio do modo de escrita. TryEnterReadLock(Int32) tenta entrar no estado de bloqueio do modo leitura, com a opção de selecionar um tempo limite inteiro. O EnterWriteLock tenta entrar no estado de Lock em Modo de Escrita. TryEnterWriteLock(Int32) tenta entrar no estado de bloqueio do modo de escrita, e o tempo de espera pode ser selecionado. Existem 2 maneiras de sair do modo de escrita/leitura: O ExitReadLock reduz a contagem recursiva do modo de leitura e sai do modo de leitura quando a contagem resultante é 0 (zero). O ExitWriteLock reduz a contagem recursiva do padrão de escrita e sai do modo de escrita quando a contagem resultante é 0 (zero). Veja como usar:
Você pode ver que a thread 3 e a thread 4 podem entrar no modo de leitura ao mesmo tempo, enquanto a thread 5 pode entrar no modo de escrita após 5 segundos (ou seja, após as threads 3 e 4 saírem do bloqueio de leitura). Modifique o código acima, primeiro abra 2 threads no modo de escrita e depois abra as threads no modo leitura, o código é o seguinte:
Os resultados são os seguintes:
Como você pode ver, a thread 3 e a thread 4 entram no modo de escrita, mas a thread 3 ocupa o bloqueio de gravação primeiro, então a thread 4 precisa esperar 10 segundos antes de entrar. As threads 5 e 6 precisam ocupar o lock de leitura, então espere a thread 4 sair do bloqueio de escrita antes de continuar. TryEnterReadLock e TryEnterWriteLock podem definir um timeout; ao executar essa frase, a thread vai bloquear aqui; se o bloqueio puder ser ocupado nesse momento, então retorne true, se o tempo de timeout ainda não ocupou o lock, então retorne false, desista da ocupação do lock e continue executando o código a seguir diretamente. EnterUpgradeableReadLock A classe ReaderWriterLockSlim oferece um modo de leitura atualizável, que difere do modo de leitura porque também pode ser atualizado para o modo de escrita chamando os métodos EnterWriteLock ou TryEnterWriteLock. Porque apenas uma thread pode estar em modo atualizável por vez. Uma thread que entra no modo upgradável não afetará a thread no modo de leitura, ou seja, quando uma thread entra no modo upgradável, qualquer número de threads pode entrar no modo de leitura ao mesmo tempo sem bloqueio. Se múltiplas threads já estiverem esperando para adquirir um bloqueio de escrita, executar o EnterUpgradeableReadLock bloqueará até que essas threads expirem ou saiam do bloqueio de escrita. O código a seguir mostra como atualizar para um bloqueio de escrita no modo de leitura atualizável.
O impacto dos bloqueios de leitura/escrita no desempenho é óbvio. O seguinte código de teste:
O código acima simula a operação de 500 Tarefas, cada uma ocupando um grupo de threads, das quais 20 são threads de escrita e 480 threads de leitura são simulados. Leva 10 ms para ler os dados e 100 ms para escrever para testar o método de bloqueio e o método ReaderWriterLockSlim, respectivamente. Uma estimativa pode ser feita, para o ReaderWriterLockSlim, assumindo que 480 threads são lidas ao mesmo tempo, então consome 10ms, 20 operações de escrita ocupam 2000ms, então o tempo consumido é 2010ms, e para o método de lock comum, como todas são exclusivas, 480 operações de leitura ocupam 4800ms + 20 operações de escrita 2000ms = 6800ms. Os resultados mostraram uma melhora perceptível no desempenho.
|