Pergunta

Estou refatorando algum código e estou pensando sobre o uso de um lock no construtor da instância.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

Por favor confirme

  1. Os construtores de instância são thread-safe.
  2. A instrução lock impede o acesso a esse bloco de código, não ao membro 'contador' estático.

Se a intenção do programador original fosse fazer com que cada instância soubesse sua 'contagem', como eu sincronizaria o acesso ao membro 'contador' para garantir que outro thread não fosse novo? MyClass e alterar a contagem antes que esta defina sua contagem?

Para sua informação - Esta classe não é única.As instâncias devem simplesmente estar cientes de seu número.

Foi útil?

Solução

@ajmastrean

Não estou dizendo que você deve usar o padrão singleton em si, mas adotar seu método de encapsular o processo de instanciação.

ou seja

  • Torne o construtor privado.
  • Crie um método de instância estático que retorne o tipo.
  • No método de instância estática, use a palavra-chave lock antes de instanciar.
  • Instancie uma nova instância do tipo.
  • Aumente a contagem.
  • Desbloqueie e devolva a nova instância.

EDITAR

Um problema que me ocorreu: como você saberia quando a contagem caiu?;)

EDITAR NOVAMENTE

Pensando nisso, você poderia adicionar código no destruidor que chama outro método estático para decrementar o contador :D

Outras dicas

Se você está apenas incrementando um número, existe uma classe especial (Interlocked) para isso...

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

Método de incremento intertravado

Incrementa uma variável especificada e armazena o resultado, como uma operação atômica.

System.Threading.Interlocked.Increment(myField);

Mais informações sobre as melhores práticas de segmentação...

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

Suponho que seja para um padrão singleton ou algo parecido.O que você quer fazer não é bloquear seu objeto, mas bloquear o contador enquanto você o modifica.

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

Porque seu código atual é meio redundante.Especialmente estando no construtor onde há apenas um thread que pode chamar um construtor, ao contrário dos métodos onde ele pode ser compartilhado entre threads e acessado a partir de qualquer thread que seja compartilhado.

Pelo pouco que posso dizer do seu código, você está tentando fornecer ao objeto a contagem atual no momento de sua criação.Portanto, com o código acima, o contador será bloqueado enquanto o contador é atualizado e configurado localmente.Portanto, todos os outros construtores terão que esperar a liberação do contador.

Você pode usar outro objeto estático para bloqueá-lo.

private static Object lockObj = new Object();

e bloqueie este objeto no construtor.

lock(lockObj){}

No entanto, não tenho certeza se há situações que devam ser tratadas devido à otimização do compilador em .NET como no caso de java

A maneira mais eficiente de fazer isso seria usar a operação de incremento Interlocked.Ele irá incrementar o contador e retornar o valor recém-definido do contador estático de uma só vez (atomicamente)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

No seu exemplo original, a instrução lock(this) não terá o efeito desejado porque cada instância individual terá uma referência "this" diferente e, portanto, várias instâncias poderão atualizar o membro estático ao mesmo tempo.

De certa forma, os construtores podem ser considerados thread-safe porque a referência ao objeto que está sendo construído não é visível até que o construtor seja concluído, mas isso não adianta nada para proteger uma variável estática.

(Mike Schall teve a parte interligada primeiro)

Eu acho que se você modificar o Padrão Singleton para incluir uma contagem (obviamente usando o método thread-safe), você ficará bem :)

Editar

Merda, eu apaguei acidentalmente!

Não tenho certeza se construtores de instância SÃO thread-safe, lembro-me de ter lido sobre isso em um livro de padrões de design, você precisa garantir que os bloqueios estejam em vigor durante o processo de instanciação, puramente por causa disso.

@Roubar

Para sua informação, esta classe pode não ser singleton, preciso de acesso a diferentes instâncias.Eles devem simplesmente manter uma contagem.Que parte do padrão singleton você mudaria para realizar o incremento do 'contador'?

Ou você está sugerindo que eu exponha um método estático para construção bloqueando o acesso ao código que incrementa e lê o contador com um bloqueio.

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top