Construindo melhores coleções thread-safe
Há alguns problemas fudamentais com a maioria das coleçãoes thread-safe. Enquanto as operações individuais são thread-safe, as operações não são geralmente combináveis. Operações comuns como a verficação da contagem de elementos em uma pilha antes de se retirar um item do seu topo (operação “pop”) são perigosas. Há APIs que tentam combinar operações como o .NET 4’s Coordination Data Structures, mas que acabam disponibilzando métodos desajeitados como o TryDequeue.
Outra tentativa foi observada nas coleções do .NET 1. Ao invés de fazer travas (locks) internos, elas foram expostas através da propriedade SyncRoot. Apesar de SyncRoot continuar sendo o nome padrão para objetos de sincronização, o pattern SyncRoot/Wrapper foi descontinuado no .NET 2..
Então como criar APIs combináveis que são realmente úteis? Jared Parsons propõe que não se exponha a API diretamente. Ao invés disso, deve-se expor todos os métodos através de um objeto temporário que é criado e só está disponível enquanto o código está em posse de uma trava no objeto. Este objeto temporário é a chave (key) na coleção e apenas seu detentor pode conseguir acesso ao seu conteúdo (value).
Aqui está um exemplo de uma fila thread-safe do Jared Parson:
static void Example1(ThreadSafeQueuequeue) {
using (var locked = queue.Lock()) {
if (locked.Count > 0) {
var first = locked.Dequeue();
}
}
}
O objeto chamado locked não é thread-safe por si mesmo e espera-se que os desenvolvedores façam a implementação correta ao utilizá-lo dentro de um bloco using. Mas contanto que eles obedeçam esta regra simples, operações dentro do bloco using serão thread-safe. O Jared comenta sobre isso:
Assim como na maioria das arquiteturas thread-safe, há vários de se utilizar este código incorretamente
Utilizar uma instância de ILockedQueueapós ela ter sido encerrada. Este erro já é considerado um tabu e pode-se confiar no conhecimento dos desenvolvedores e acreditar que ele não será cometido. Além disso, ferramentas de análise estática de código como a FxCop vão indicar isto como um erro. Se o desenvolvedor tiver um pouco mais de rigor, isso pode-se prevenir que ele não acontecerá. Basta simplesmente adicionar um flag “disposed” e verificá-lo na entrada de qualquer método. É possível que o desenvolvedor mantenha os valores internos da coleção, como a contagem entre as chamados de travamento (lock) e utilizá-los mais tarde para fazer suposições imprecisas sobre o estado da coleção. Se o desenvolvedor não descartar a instância do ILockedQueue, ela ficará travada para sempre. Felizmente, o FxCop é capaz de indicar isso como um erro uma vez que é uma implementação da IDisposable. Entretanto, essa verificação não é a prova de falhas. Nada diz ao usuário “por favor, utilize o IlockedQueueapenas por um período curto de tempo”. Uma IDisposable por si só transmite esta idéia, mas certamente isso não é perfeito. A implementação do ILockedQueuenão é thread-safe. Idealmente, os desenvolvedores não deveriam passar instâncias de uma IDisposable entre threads, mas isso é algo a se pensar.
Conteúdo educacional
Mobilidade: Frameworks, SOs e o Mercado
Ricardo Ogliari 23 Mai, 2013
Caminhos de uma estratégia mobile
Sérgio Lopes 23 Mai, 2013
Complexidade organizacional no Século 21
Alexandre Magno 16 Mai, 2013

Olá visitante
Você precisa cadastrar-se no InfoQ Brasil ou Login para enviar comentários. Há muitas vantagens em se cadastrar.Obtenha o máximo da experiência do InfoQ Brasil.
Dê sua opinião