Eu quero uma implementação de List<T>
como uma propriedade que pode ser usada com segurança em thread sem qualquer dúvida.
Algo assim:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Parece que ainda preciso retornar uma cópia (clonada) da coleção, portanto, se em algum lugar estivermos iterando a coleção e, ao mesmo tempo, a coleção for definida, nenhuma exceção será gerada.
Como implementar uma propriedade de coleção thread-safe?
c#
collections
properties
thread-safety
Xaqron
fonte
fonte
IList<T>
(vsList<T>
)?List<T>
implementa? Em caso afirmativo, você poderia fornecer uma interface de que precisa em vez de perguntar sobre tudo o queList<T>
já tem?Respostas:
Se você estiver direcionando .Net 4, há algumas opções no namespace System.Collections.Concurrent
Você pode usar
ConcurrentBag<T>
neste caso, em vez deList<T>
fonte
ConcurrentBag
é uma coleção não ordenada, portanto, ao contrárioList<T>
, não garante a ordenação. Além disso, você não pode acessar itens por índice.Mesmo tendo a maioria dos votos, geralmente não se pode pegar
System.Collections.Concurrent.ConcurrentBag<T>
como um substituto seguro para threadSystem.Collections.Generic.List<T>
, pois (Radek Stromský já apontou) não ordenado.Mas existe uma classe chamada
System.Collections.Generic.SynchronizedCollection<T>
que já faz parte do framework desde .NET 3.0, mas está bem escondida em um local onde não se espera que seja pouco conhecida e provavelmente você nunca tropeçou nela (pelo menos Eu nunca fiz).SynchronizedCollection<T>
é compilado no assembly System.ServiceModel.dll (que faz parte do perfil do cliente, mas não da biblioteca de classes portátil).Espero que ajude.
fonte
Eu acho que criar uma classe ThreadSafeList de amostra seria fácil:
Você simplesmente clona a lista antes de solicitar um enumerador e, portanto, qualquer enumeração está trabalhando em uma cópia que não pode ser modificada durante a execução.
fonte
T
for um tipo de referência, isso não retornará apenas uma nova lista contendo referências a todos os objetos originais? Se for esse o caso, essa abordagem ainda pode causar problemas de threading, pois os objetos da lista podem ser acessados por vários threads por meio de diferentes "cópias" da lista.newList
, nenhum item adicionado ou removido que invalidaria o enumerador).Mesmo a resposta aceita é ConcurrentBag, não acho que seja uma substituição real de lista em todos os casos, como o comentário de Radek à resposta diz: "ConcurrentBag é uma coleção não ordenada, portanto, ao contrário de List, não garante a ordenação. Além disso, você não pode acessar itens por índice "
Portanto, se você usar o .NET 4.0 ou superior, uma solução alternativa poderia ser usar ConcurrentDictionary com o inteiro TKey como índice de array e TValue como valor de array. Esta é a maneira recomendada de substituir a lista no curso C # Coleções simultâneas da Pluralsight . ConcurrentDictionary resolve os dois problemas mencionados acima: acesso e ordenação do índice (não podemos confiar na ordenação, pois é uma tabela hash subjacente, mas a implementação atual do .NET salva a ordem de adição de elementos).
fonte
ConcurrentDictionary
é um dicionário, não uma lista. 2) Não é garantido que a ordem seja preservada, como afirma sua própria resposta, o que contradiz sua razão declarada para postar uma resposta. 3) Tem um link para um vídeo sem incluir as citações relevantes nesta resposta (o que pode não estar de acordo com o licenciamento, de qualquer maneira).current implementation
se não for explicitamente garantido pela documentação. A implementação pode mudar a qualquer momento sem aviso prévio.A
ArrayList
classe C # tem umSynchronized
método.Isso retorna um wrapper thread-safe em torno de qualquer instância de
IList
. Todas as operações precisam ser realizadas através do wrapper para garantir a segurança do thread.fonte
Se você olhar o código-fonte para List of T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), você perceberá que há uma classe lá (que é claro interno - por que, Microsoft, por quê?!?!) chamado SynchronizedList de T. Estou copiando colando o código aqui:
Pessoalmente, acho que eles sabiam que uma implementação melhor usando o SemaphoreSlim poderia ser criada, mas não chegaram a ela.
fonte
_root
) em cada acesso (leitura / gravação) torna esta uma solução lenta. Talvez seja melhor para essa classe permanecer interna.Clear()
após outras chamadas,this[index]
mas antes que o bloqueio seja ativado.index
não é mais seguro de usar e lançará uma exceção quando finalmente for executado.Você também pode usar o mais primitivo
qual bloqueio usa (veja este post C # Bloqueando um objeto que é reatribuído no bloco de bloqueio ).
Se você está esperando exceções no código, isso não é seguro, mas permite que você faça algo como o seguinte:
Uma das coisas boas sobre isso é que você obterá o bloqueio durante a série de operações (em vez de bloquear em cada operação). O que significa que a saída deve vir nas partes certas (meu uso disso foi obter alguma saída na tela de um processo externo)
Eu realmente gosto da simplicidade + transparência do ThreadSafeList + que faz a parte importante para parar travamentos
fonte
No .NET Core (qualquer versão), você pode usar ImmutableList , que possui todas as funcionalidades do
List<T>
.fonte
Eu acredito que
_list.ToList()
vou te fazer uma cópia. Você também pode consultá-lo se precisar, como:De qualquer forma, o msdn diz que isso é de fato uma cópia e não simplesmente uma referência. Ah, e sim, você precisará bloquear o método definido como os outros apontaram.
fonte
Parece que muitas das pessoas que estão achando isso estão querendo uma coleção de tamanho dinâmico indexada com thread safe. A coisa mais próxima e fácil que conheço é.
Isso exigiria que você garantisse que sua chave fosse devidamente incriminada, se você deseja um comportamento de indexação normal. Se você for cuidadoso, .count pode ser suficiente como a chave para quaisquer novos pares de valores-chave que você adicionar.
fonte
Eu sugeriria que qualquer pessoa que lida com
List<T>
cenários de multi-threading dê uma olhada em Immutable Collections, em particular o ImmutableArray .Achei muito útil quando você tem:
Também pode ser útil quando você precisa implementar algum tipo de comportamento semelhante a uma transação (ou seja, reverter uma operação de inserção / atualização / exclusão em caso de falha)
fonte
Aqui está a aula que você pediu:
fonte
this.GetEnumerator();
quando @Tejs sugerethis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?Basicamente, se você deseja enumerar com segurança, você precisa usar o bloqueio.
Consulte o MSDN sobre isso. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Aqui está uma parte do MSDN que você pode estar interessado:
Membros públicos estáticos (compartilhados no Visual Basic) desse tipo são thread-safe. Não é garantido que nenhum membro da instância seja thread-safe.
Uma lista pode oferecer suporte a vários leitores simultaneamente, desde que a coleção não seja modificada. Enumerar por meio de uma coleção não é intrinsecamente um procedimento thread-safe. No caso raro em que uma enumeração contende com um ou mais acessos de gravação, a única maneira de garantir a segurança do thread é bloquear a coleção durante toda a enumeração. Para permitir que a coleção seja acessada por vários threads para leitura e gravação, você deve implementar sua própria sincronização.
fonte
Aqui está a classe para lista de thread segura sem bloqueio
fonte
Use a
lock
declaração para fazer isso. ( Leia aqui para obter mais informações. )Para sua informação, isso provavelmente não é exatamente o que você está perguntando - você provavelmente deseja bloquear mais em seu código, mas não posso assumir isso. Dê uma olhada no
lock
palavra chave e adapte seu uso à sua situação específica.Se você precisar, você pode
lock
no blocoget
eset
usando a_list
variável que faria com que uma leitura / gravação não pudesse ocorrer ao mesmo tempo.fonte
lock
declaração. Usando um exemplo semelhante, eu poderia argumentar que não devemos usar loops for, uma vez que você pode travar o aplicativo com quase nenhum esforço:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
elock (typeof(this))
são grandes proibições.