¿Versión C # de la palabra clave sincronizada de Java?

313

¿C # tiene su propia versión de la palabra clave "sincronizada" de Java?

Es decir, en Java se puede especificar a una función, un objeto o un bloque de código, así:

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

o

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
fuente
3
El formulario de bloque requiere una referencia para bloquear. En la forma del método, el objeto de bloqueo es implícitamente esto (o la Clase [this.class, no getClass ()] para métodos estáticos, pero no se bloquea en las Clases).
Tom Hawtin - tackline
1
¿Todavía no está protegido? Siempre vengo aquí porque no recuerdo esa [MethodImpl(MethodImplOptions.Synchronized)]línea.
Bitterblue
1
Creo que su segundo fragmento no se compilaría, debe sincronizarse con algo.
PoweredByRice

Respuestas:

466

Primero: la mayoría de las clases nunca necesitarán ser seguras para subprocesos. Use YAGNI : solo aplique seguridad de hilo cuando sepa que realmente lo va a usar (y probarlo).

Para las cosas de nivel de método, hay [MethodImpl]:

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

Esto también se puede usar en accesores (propiedades y eventos):

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

Tenga en cuenta que los eventos similares a campos se sincronizan de manera predeterminada, mientras que las propiedades implementadas automáticamente no lo son :

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

Personalmente, no me gusta la implementación MethodImplya que se bloquea thiso typeof(Foo), lo que va en contra de las mejores prácticas. La opción preferida es usar sus propios bloqueos:

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

Tenga en cuenta que para eventos de tipo campo, la implementación de bloqueo depende del compilador; en los compiladores de Microsoft más antiguos es un lock(this)/ lock(Type), sin embargo, en los compiladores más recientes usaInterlocked actualizaciones, por lo que es seguro para subprocesos sin las partes desagradables.

Esto permite un uso más granular y permite el uso de Monitor.Wait/ Monitor.Pulseetc para comunicarse entre subprocesos.

Una entrada de blog relacionada (luego revisada ).

Marc Gravell
fuente
@earcam y tu pregunta es? Esa afirmación es cierta. La gran mayoría de las clases no tienen el requisito de ser seguros para los hilos, no se probará la seguridad de los hilos y tener seguridad de los hilos afectará el rendimiento. El número de tipos que realmente deben preocuparse por los hilos es muy pequeño: colecciones sincronizadas intencionalmente, multiplexores, etc.
Marc Gravell
44
Creo que debería haber dicho simplemente; "la mayoría de las clases nunca necesitarán ser seguras para subprocesos" pero "todos los desarrolladores deben tener en cuenta la concurrencia". En retrospectiva, estoy de acuerdo en que el número es muy pequeño (y definitivamente es algo que desea acertar una vez en un lugar, lo que permite que la mayoría de las clases interactúen ajenas a su entorno de subprocesos múltiples). Ojalá hubiera eliminado el comentario más rápido =)
earcam
66
La publicación de blog vinculada de Marc tiene un seguimiento de marzo de 2010 que dice que en .NET 4.0, MethodImply los eventos de campo ahora generan un buen código de sincronización, y ya no es necesario usar sus propios bloqueos.
Rory O'Kane
2
En la actualidad, una buena mayoría de las aplicaciones se basan en la web y se sirven con marcos basados ​​en la reutilización de instancias pesadas y el ciclo de vida complejo de los objetos a través de la inyección de dependencia. La mentalidad predeterminada en estos días tiende a errar al lado de la seguridad de subprocesos.
Sheepy
1
@Elazar es demasiado tarde ahora, pero para el registro: cambiar el marco es un cambio de una línea a csproj si lo ha creado usando las plantillas .net estándar / .net core, y .net regular está disponible, ya que es multi -targeting. Sin embargo, la herramienta IDE en torno a esto es simplemente terrible: solo necesita saber qué puede cambiar y qué :)
Marc Gravell
56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Jan Gressmann
fuente
10
¿Está seguro de que desea declarar su objeto de bloqueo como estático ...?
serg10
21
Claro, por lo que cada subproceso puede acceder fácilmente sin pasar referencias.
Jan Gressmann
33
Si estamos en el contexto de la pregunta del autor de la pregunta, estamos hablando de métodos de instancia. El uso de estática significa que si el subproceso 1 llama a instancia1.DoSomething () y el subproceso 2 llama a instancia2.DoSomething, la segunda llamada se bloqueará aunque sea un objeto completamente diferente. La llamada de thread2 no debería bloquearse a menos que alguien llame a DoSomething el mismo objeto . No digo que esté equivocado, pero dice que es importante comprender el efecto del uso de estática aquí, porque puede causar un bajo rendimiento al bloquear globalmente en lugar de por instancia.
AaronLS
1
@AaronLS El bloqueo estático es muy útil cuando su objeto realiza acciones en un ámbito mayor que él mismo. Siempre sucede con los servicios web, por ejemplo.
Thibault D.
44
-1 ya que este es un comportamiento diferente al que está solicitando el OP. Este es un bloqueo de clase, no un bloqueo de instancia.
tster
39

¿C # tiene su propia versión de la palabra clave "sincronizada" de Java?

No. En C #, explícitamente lockrecursos en los que desea trabajar sincrónicamente a través de subprocesos asincrónicos. lockabre un bloque No funciona a nivel de método.

Sin embargo, el mecanismo subyacente es similar ya que lockfunciona invocando Monitor.Enter(y posteriormenteMonitor.Exit ) en el tiempo de ejecución. Java funciona de la misma manera, de acuerdo a la documentación de Sun .

Konrad Rudolph
fuente
3
No tiene una "palabra clave" equivalente, pero como muestra la respuesta anterior de Marc Gravell, puede sincronizar a nivel de método utilizando la anotación [MethodImpl (MethodImplOptions.Synchronized)].
MindJuice
1
Dado que synchronizedel método on de Java es básicamente synchronized (this.getClass())¿no sería similar en C # lock(typeof(this))?
Sri Harsha Chilakapati
2
@SriHarshaChilakapati eso es solo parcialmente correcto, la synchronizedpalabra clave de java en un método es más como:, synchronized(this)solo en un método estático se comporta como synchronized(class).
bvdb
6

Tome nota, con caminos completos la línea: [MethodImpl(MethodImplOptions.Synchronized)]debería verse como

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

Traubenfuchs
fuente
1
o simplemente puede usarlousing System.Runtime.CompilerServices;
aloisdg se muda a codidact.com el
Escribí ese comentario cuando aún no sabía cómo insertar automáticamente usando declaraciones, después de haber programado C # por no más de unos pocos días o semanas y estoy sorprendido por esos 3 votos a favor.
Traubenfuchs
1
Usted ayudó a por lo menos 3 desarrolladores y que es agradable :)
movimiento aloisdg a codidact.com
5

Puede usar la lockdeclaración en su lugar. Creo que esto solo puede reemplazar la segunda versión. Además, recuerde que tanto synchronizedy locknecesidad de trabajar con un objeto.

James
fuente