Eu tenho uma lista de usuários em cache no meu aplicativo. Algumas páginas de administração precisam da lista de todos os usuários (o total é pequeno o suficiente, isso não é um problema).
A abordagem que estou adotando é:
- Quando a lista em cache for solicitada, essa lista não será alterada.
- Quando um usuário é CrUD, há uma chamada para adicioná-lo ao cache.
- Quando essa chamada é feita, a lista existente é copiada para a nova lista e o usuário CrUD é adicionado/atualizado/removido da lista. E essa lista é então a nova lista em cache.
Boa ideia. Mas acabei de escrever alguns testes de unidade que fazem isso e não funcionam. Porque 2 threads estão reconstruindo a lista e aquele que a escreve em segundo lugar vence. Posso fazer uma lock (this)
reconstrução da lista e isso funciona muito bem.
Mas essa é uma abordagem terrível. Ótimo para identificar o problema, mas não é bom na produção.
Em uso normal, essa abordagem de qualquer lista retornada é imutável e funciona muito bem. Como posso fazer isso sem um lock
?
Em C#, mas esta é uma questão independente de linguagem, embora possa haver alguma classe C#, etc., que torna esta uma solução fácil.
Você não pode. Pelo menos não se você quiser usar um
List<T>
que contenha objetos mutáveisUser
. AList<T>
coleção não é segura para threads. Mesmo que fosse, a mutação dosUser
elementos que ele contém sem sincronização resultaria em um comportamento com erros/indefinido.Não necessariamente. Se você fizer uso cuidadoso da
lock
declaração, o que significa que você protege apenas a interação com o estado compartilhado (a lista e os usuários) e não a reconstrução da nova lista antes que ela seja compartilhada, a contenção provavelmente será mínima e o desempenho bastante decente.É claro que existem outras opções disponíveis, que não envolvem um arquivo
List<T>
. Por exemplo:ConcurrentDictionary<K,V>
atualizado comGetOrAdd
/AddOrUpdate
.ImmutableList<T>
atualizado comImmutableInterlocked.Update
.Ambas as alternativas de bloqueio baixo exigem que o
User
tipo seja imutável. Eles não apenas terão um desempenho melhor (provavelmente), mas também serão mais convenientes.Exemplo com a
ConcurrentDictionary<K,V>
erecord class
asUser
:Demonstração on-line .