Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Objeto
Nota
Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos e se incorporan en la especificación ECMA actual.
Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.
Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones de .
Problema planteado por experto: https://github.com/dotnet/csharplang/issues/7104
Resumen
Caso especial de cómo interactúa System.Threading.Lock con la palabra clave lock (llamando al método EnterScope en segundo plano).
Agregue advertencias de análisis estáticos para evitar un uso incorrecto accidental del tipo siempre que sea posible.
Motivación
.NET 9 presenta un nuevo tipo de System.Threading.Lock como mejor alternativa al bloqueo basado en monitor existente.
La presencia de la palabra clave lock en C# podría llevar a los desarrolladores a pensar que pueden usarla con este nuevo tipo.
Si lo hace, no se bloquearía según la semántica de este tipo, sino que lo trataría como cualquier otro objeto y usaría el bloqueo basado en el monitor.
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
Diseño detallado
La semántica de la instrucción de lock (§13.13) se modifica para tratar de manera especial el tipo System.Threading.Lock:
Instrucción
lockde la formalock (x) { ... }
- donde
xes una expresión de tipoSystem.Threading.Lock, es exactamente equivalente a:yusing (x.EnterScope()) { ... }System.Threading.Lockdeben tener la siguiente forma:namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }- donde
xes una expresión de un reference_type, es exactamente equivalente a: [...]
Tenga en cuenta que es posible que la forma no esté totalmente activada (por ejemplo, no habrá errores ni advertencias si el tipo Lock no es sealed), pero es posible que la característica no funcione según lo previsto (por ejemplo, no habrá advertencias al convertir Lock a un tipo derivado, ya que la característica supone que no hay ningún tipo derivado).
Además, se han añadido nuevas advertencias a conversiones de referencia implícitas (§10.2.8) al convertir el tipo System.Threading.Lock:
Las conversiones de referencia implícitas son:
- De reference_type a
objectydynamic.
- Se genera una advertencia cuando se sabe que el reference_type es
System.Threading.Lock.- De cualquier class_type
Sa cualquier class_typeT, siempre queSse derive deT.
- Se notifica una advertencia cuando se sabe que
SesSystem.Threading.Lock.- Desde cualquier class_type
Sa cualquier interface_typeT, siempre queSimplementeT.
- Se notifica una advertencia cuando se sabe que
SesSystem.Threading.Lock.- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
Tenga en cuenta que esta advertencia se produce incluso para conversiones explícitas equivalentes.
El compilador evita notificar la advertencia en algunos casos cuando la instancia no se puede bloquear después de convertir en object:
- cuando la conversión es implícita y forma parte de una invocación de operador de igualdad de objetos.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
Para saltarse la advertencia y forzar el uso del bloqueo basado en el monitor, se puede emplear lo siguiente
- los medios habituales de eliminación de advertencias (
#pragma warning disable), - las API de
Monitordirectamente, o - la conversión indirecta como
object AsObject<T>(T l) => (object)l;.
Alternativas
Admita un patrón general que otros tipos también pueden usar para interactuar con la palabra clave
lock. Esto es algo que se tiene previsto implementar cuando las variablesref structs puedan intervenir en los genéricos. Hay un hilo explicativo sobre esto en LDM 2023-12-04.Para evitar ambigüedad entre el bloqueo basado en el monitor existente y el nuevo
Lock(o patrón en el futuro), podríamos:- Introduce una nueva sintaxis en lugar de reutilizar la instrucción
lockexistente. - Requerir que los nuevos tipos de bloqueo sean
structs (ya que ellockexistente no permite los tipos de valor). Podría haber problemas con los constructores predeterminados y las copias si las estructuras tienen una inicialización diferida.
- Introduce una nueva sintaxis en lugar de reutilizar la instrucción
La generación de código se podría proteger frente a anulaciones de subprocesos (que están obsoletas).
También podríamos avisar cuando se pasa
Lockcomo parámetro de tipo, ya que el bloqueo en un parámetro de tipo siempre utiliza un bloqueo basado en el monitor:M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }Sin embargo, esto provocaría advertencias al almacenar
Locken una lista que no es deseable:List<Lock> list = new(); list.Add(new Lock()); // would warn herePodríamos incluir análisis estáticos para evitar el uso de
System.Threading.Locken variablesusings conawaits. Por ejemplo, podríamos emitir un error o una advertencia para código comousing (lockVar.EnterScope()) { await ... }. Actualmente, esto no es necesario, ya queLock.Scopees unref struct, por lo que el código es ilegal de todos modos. Sin embargo, si alguna vez permitimosref structen métodosasynco cambiamosLock.Scopepara que deje de ser unref struct, este análisis podría ser beneficioso. (Es probable que también tengamos que considerar todos los tipos de bloqueo que coincidan con el patrón general si se implementa en el futuro. Aunque es posible que deba haber un mecanismo de exclusión, ya que es posible que algunos tipos de bloqueo se puedan usar conawait). Como alternativa, esto podría implementarse como analizador enviado como parte del entorno de ejecución.Podríamos relajar la restricción de que los tipos de valor no se puedan bloquear (
lock)- para el nuevo tipo
Lock(es necesario solo si la propuesta de la API lo cambió declassastruct), - en el patrón general en el que cualquier tipo puede intervenir cuando se implemente en el futuro.
- para el nuevo tipo
Podríamos permitir el nuevo
locken métodosasyncen los queawaitno se usa dentro dellock.- Actualmente, dado que
lockse reduce ausingcon unref structcomo recurso, se produce un error en tiempo de compilación. La solución consiste en extraer ellocken un método noasyncindependiente. - En lugar de usar el
ref struct Scope, podríamos emitir métodosLock.EnteryLock.Exitentry/finally. Sin embargo, el métodoExitdebe iniciarse cuando se llama a partir de un subproceso diferente deEnter, ya que incluye una búsqueda de subprocesos que se evita al usar elScope. - Lo mejor sería que se permitiera la compilación de
usingen unref structen métodosasyncsi no hayawaitdentro del cuerpo deusing.
- Actualmente, dado que
Reuniones de diseño
- LDM 2023-05-01: Decisión inicial de incorporar un patrón
lock - LDM 2023-10-16: Probado en el espacio de trabajo de .NET 9
- LDM 2023-12-04: Se ha rechazado el patrón general, solo se ha aceptado los casos especiales del tipo
Locky se han añadido advertencias de análisis estático
C# feature specifications