Versão c # da palavra-chave sincronizada do java?

313

O c # tem sua própria versão da palavra-chave "sincronizada" do java?

Ou seja, em java, pode ser especificado para uma função, um objeto ou um bloco de código, assim:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}

ou

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
fonte
3
O formulário do bloco requer uma referência para bloquear. No formulário do método, o objeto lock é implicitamente esse (ou a classe [this.class, não getClass ()] para métodos estáticos, mas não trava nas classes).
Tom Hawtin - tackline
1
Ainda não está protegido? Eu sempre venho aqui porque não me lembro dessa [MethodImpl(MethodImplOptions.Synchronized)]frase.
Bitterblue
1
Acho que o seu segundo trecho não seria compilado - ele precisa ser sincronizado com alguma coisa.
PoweredByRice

Respostas:

466

Primeiro - a maioria das aulas nunca precisará ser segura para threads. Use YAGNI : aplique a segurança de threads apenas quando você souber que realmente vai usá-lo (e testá-lo).

Para o material no nível do método, existe [MethodImpl]:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {/* code */}

Isso também pode ser usado em acessadores (propriedades e eventos):

private int i;
public int SomeProperty
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return i; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { i = value; }
}

Observe que os eventos do tipo campo são sincronizados por padrão, enquanto as propriedades implementadas automaticamente não são :

public int SomeProperty {get;set;} // not synchronized
public event EventHandler SomeEvent; // synchronized

Pessoalmente, não gosto da implementação de MethodImplcomo ela bloqueia thisou typeof(Foo)- o que é contra as melhores práticas. A opção preferida é usar seus próprios bloqueios:

private readonly object syncLock = new object();
public void SomeMethod() {
    lock(syncLock) { /* code */ }
}

Observe que, para eventos do tipo campo, a implementação de bloqueio depende do compilador; em compiladores mais antigos da Microsoft, é um lock(this)/ lock(Type)- no entanto, em compiladores mais recentes, ele usaInterlocked atualizações -, portanto, são seguros para threads sem as partes desagradáveis.

Isso permite um uso mais granular e permite o uso de Monitor.Wait /Monitor.Pulse etc para se comunicar entre os threads.

Uma entrada de blog relacionada ( revisitada posteriormente ).

Marc Gravell
fonte
@earcam e sua pergunta é? Essa afirmação é verdadeira. A grande maioria das classes têm nenhuma exigência para ser thread-safe, não será testado para thread-segurança, e tendo thread-segurança irá afetar o desempenho. O número de tipos que realmente precisam se preocupar com threads é muito pequeno - coleções intencionalmente sincronizadas, multiplexadores etc.
Marc Gravell
4
Eu acho que deveria ter simplesmente declarado; "a maioria das classes nunca precisará ser segura para threads", mas "todos os desenvolvedores devem estar cientes da concorrência". Em retrospecto, eu concordo que o número é muito pequeno (e definitivamente algo que você deseja acertar uma vez em um só lugar, permitindo que a maioria das classes interaja alheia ao ambiente com vários segmentos). Desejo que eu tinha eliminado o comentário mais rápida =)
earcam
6
A postagem do blog vinculado por Marc tem um acompanhamento de março de 2010 dizendo que no .NET 4.0 MethodImple eventos do tipo campo agora geram um bom código de sincronização e não é mais necessário usar seus próprios bloqueios.
Rory O'Kane
2
Atualmente, uma boa maioria de aplicativos é baseada na Web, servida com estruturas que dependem de reutilização de instância pesada e ciclo de vida de objetos complexos via injeção de dependência. A mentalidade padrão hoje em dia tende a errar ao lado da segurança da rosca.
Sheepy
1
@ Elazar é tarde demais agora, mas para constar: alterar a estrutura é uma alteração de uma linha para o csproj, se você a criou usando os modelos padrão .net / .net - e o .net regular está disponível, -alvejando. No entanto, as ferramentas IDE são simplesmente terríveis - você só precisa saber o que pode mudar e o que :)
Marc Gravell
56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Jan Gressmann
fonte
10
Tem certeza de que deseja declarar seu objeto de bloqueio como estático ..?
serg10
21
Claro, para que cada Thread possa acessá-lo facilmente, sem passar referências.
Jan Gressmann
33
Se estamos no contexto da pergunta do autor, estamos falando de métodos de instância. Usar estático significa que se o segmento 1 chama instance1.DoSomething () e o segmento 2 chama instance2.DoSomething, a segunda chamada será bloqueada, mesmo sendo um objeto completamente diferente. A chamada do thread2 não deve bloquear, a menos que alguém esteja chamando DoSomething no mesmo objeto . Não dizendo que você está errado, mas dizendo que é importante entender o efeito do uso de estática aqui, porque isso pode causar um desempenho ruim ao bloquear globalmente em vez de por instância.
AaronLS
1
@AaronLS O bloqueio estático se for muito útil quando o objeto executar ações em um escopo maior que ele. Sempre acontece com serviços da web, por exemplo.
Thibault D.
4
-1, pois esse é um comportamento diferente do solicitado pelo OP. Este é um bloqueio de classe, não um bloqueio de instância.
tster 25/10
39

O c # tem sua própria versão da palavra-chave "sincronizada" do java?

Não. No C #, você explicitamente lockrecursos nos quais deseja trabalhar de forma síncrona em threads assíncronos.lockabre um bloco; não funciona no nível do método.

No entanto, o mecanismo subjacente é semelhante, pois lockfunciona invocando Monitor.Enter(e subseqüentemente Monitor.Exit) no tempo de execução. Java funciona da mesma maneira, de acordo com a documentação da Sun .

Konrad Rudolph
fonte
3
Ele não possui uma "palavra-chave" equivalente, mas, como mostra a resposta de Marc Gravell acima, você pode sincronizar no nível do método usando a anotação [MethodImpl (MethodImplOptions.Synchronized)].
precisa saber é o seguinte
1
Como o synchronizedmétodo on de Java synchronized (this.getClass())não é basicamente o semelhante em C # lock(typeof(this))?
Sri Harsha Chilakapati
2
@SriHarshaChilakapati que está parcialmente correta, a synchronizedpalavra-chave java em um método é mais parecida com:: synchronized(this)apenas em um método estático se comporta synchronized(class).
Bvdb
6

Tome nota, com caminhos completos da linha: [MethodImpl(MethodImplOptions.Synchronized)]deve parecer

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

Traubenfuchs
fonte
1
ou você pode simplesmente usarusing System.Runtime.CompilerServices;
aloisdg se mudar para codidact.com
Escrevi esse comentário quando ainda não sabia sobre a inserção automática de instruções usando, depois de ter programado o C # por não mais do que alguns dias ou semanas e fico impressionado com esses 3 votos positivos.
Traubenfuchs
1
Você ajudou a pelo menos 3 devs e isso é bom :)
movimento aloisdg para codidact.com
5

Você pode usar a lockinstrução. Eu acho que isso só pode substituir a segunda versão. Além disso, lembre-se de que ambos synchronizede lockprecisa operar em um objeto.

James
fonte