Objet Lock
Remarque
Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Il inclut les modifications de spécification proposées, ainsi que les informations nécessaires pendant la conception et le développement de la fonctionnalité. Ces articles sont publiés jusqu’à ce que les modifications de spécification proposées soient finalisées et incorporées dans la spécification ECMA actuelle.
Il peut y avoir des différences entre la spécification de la fonctionnalité et l’implémentation terminée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).
Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications .
Problème de champion : https://github.com/dotnet/csharplang/issues/7104
Résumé
Cas particulier où System.Threading.Lock
interagit avec le mot clé lock
(appel de sa méthode EnterScope
sous le capot).
Ajoutez des avertissements d’analyse statique pour éviter toute utilisation accidentelle du type, le cas échéant.
Motivation
.NET 9 introduit un nouveau type de System.Threading.Lock
comme meilleure alternative au verrouillage basé sur le moniteur existant.
La présence du mot clé lock
en C# peut amener les développeurs à penser qu’ils peuvent l’utiliser avec ce nouveau type.
Cela ne verrouillerait pas en fonction de la sémantique de ce type, mais le traiterait plutôt comme n’importe quel autre objet et utiliserait le verrouillage basé sur le moniteur.
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
Conception détaillée
La sémantique de l’instruction lock (§13.13) est modifiée pour traiter de façon spéciale le type System.Threading.Lock
:
Une instruction
lock
du formulairelock (x) { ... }
- où
x
est une expression de typeSystem.Threading.Lock
, équivaut précisément à :etusing (x.EnterScope()) { ... }
System.Threading.Lock
doivent avoir la forme suivante :namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }
- où
x
est une expression d’un reference_type, est précisément équivalente à : [...]
Notez que la forme peut ne pas être entièrement vérifiée (par exemple, il n’y aura pas d’erreurs ni d’avertissements si le type Lock
n’est pas sealed
), mais que la fonctionnalité peut ne pas fonctionner comme prévu (par exemple, il n’y aura pas d’avertissements lors de la conversion de Lock
en type dérivé, car la fonctionnalité suppose qu’il n’existe aucun type dérivé).
De plus, de nouveaux avertissements sont ajoutés aux conversions de référence implicites (§10.2.8) lors du surclassement du type System.Threading.Lock
:
Les conversions de référence implicites sont les suivantes :
- De n’importe quel reference_type à
object
etdynamic
.
- Un avertissement est signalé lorsque le reference_type est connu pour être
System.Threading.Lock
.- De n’importe quel class_type
S
à n’importe quel class_typeT
, à condition queS
soit dérivé deT
.
- Un avertissement est signalé lorsque
S
est connu pour êtreSystem.Threading.Lock
.- De n’importe quel class_type
S
à n’importe quel interface_typeT
, à condition queS
implémenteT
.
- Un avertissement est signalé lorsque
S
est connu pour êtreSystem.Threading.Lock
.- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
Notez que cet avertissement se produit même pour les conversions explicites équivalentes.
Le compilateur évite de signaler l’avertissement dans certains cas lorsque l’instance ne peut pas être verrouillée après la conversion en object
:
- lorsque la conversion est implicite et qu’elle fait partie d’un appel d’opérateur d’égalité d’objet.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
Pour échapper à l’avertissement et forcer l’utilisation du verrouillage basé sur le moniteur, il est possible d’utiliser
- les moyens habituels de suppression des avertissements (
#pragma warning disable
), - API
Monitor
directement, - diffusion indirecte comme
object AsObject<T>(T l) => (object)l;
.
Alternatives
Soutenir un modèle général que d'autres types peuvent également utiliser pour interagir avec le mot clé
lock
. Il s’agit d’un projet futur qui pourrait être implémenté lorsque desref struct
peuvent participer à des génériques. Abordé dans LDM 2023-12-04.Pour éviter toute ambiguïté entre le verrouillage basé sur le moniteur existant et la nouvelle
Lock
(ou modèle à l’avenir), nous pourrions :- Introduisez une nouvelle syntaxe au lieu de réutiliser l’instruction
lock
existante. - Exiger que les nouveaux types de verrous soient
struct
s (étant donné que lelock
existant interdit les types valeur). Il peut y avoir des problèmes avec les constructeurs par défaut et la copie si les structures utilisent une initialisation différée.
- Introduisez une nouvelle syntaxe au lieu de réutiliser l’instruction
Le générateur de code pourrait être renforcé contre les abandons de thread (qui sont eux-mêmes obsolètes).
Nous pouvons également avertir quand
Lock
est passé en tant que paramètre de type, car le verrouillage sur un paramètre de type utilise toujours le verrouillage basé sur le moniteur :M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }
Toutefois, cela entraînerait des avertissements lors du stockage de
Lock
s dans une liste qui n’est pas souhaitable :List<Lock> list = new(); list.Add(new Lock()); // would warn here
Nous pourrions inclure une analyse statique pour empêcher l’utilisation de
System.Threading.Lock
dansusing
s avecawait
s. Par exemple, nous pourrions émettre une erreur ou un avertissement pour le code commeusing (lockVar.EnterScope()) { await ... }
. Actuellement, cela n’est pas nécessaire, carLock.Scope
est unref struct
, de sorte que le code est illégal de toute façon. Toutefois, si jamais nous permettions desref struct
dans les méthodesasync
ou changionsLock.Scope
pour qu'il ne soit pas unref struct
, cette analyse deviendrait bénéfique. (Nous aurions également besoin de prendre en compte tous les types de verrous correspondant au modèle général s’ils sont implémentés à l’avenir. Bien qu’il puisse être nécessaire d’utiliser un mécanisme d’annulation, car certains types de verrous peuvent être utilisés avecawait
.) Vous pouvez également implémenter cette opération en tant qu’analyseur fourni dans le cadre du runtime.Nous pourrions assouplir la restriction selon laquelle les types de valeur ne peuvent pas être
lock
.- pour le nouveau type de
Lock
(nécessaire uniquement si la proposition d’API l’a changée declass
àstruct
), - pour le modèle général dans lequel tout type peut participer lorsqu’il est implémenté à l’avenir.
- pour le nouveau type de
Nous pourrions autoriser les nouveaux
lock
dans les méthodesasync
oùawait
n’est pas utilisé à l'intérieur delock
.- Actuellement, étant donné que
lock
est réduit àusing
avec unref struct
en tant que ressource, cela entraîne une erreur de compilation. La solution de contournement consiste à extraire lelock
dans une méthode distincte nonasync
. - Au lieu d’utiliser le
ref struct Scope
, nous pourrions émettre des méthodesLock.Enter
etLock.Exit
danstry
/finally
. Toutefois, la méthodeExit
doit déclencher une exception lorsqu'elle est appelée à partir d'un thread différent deEnter
, elle contient donc une recherche de thread qui est évitée lors de l'utilisation deScope
. - Il serait préférable de permettre de compiler
using
sur unref struct
dans les méthodesasync
s’il n’y a pas deawait
à l’intérieur du corpsusing
.
- Actuellement, étant donné que
Concevoir des réunions
- LDM 2023-05-01 : décision initiale de soutenir un modèle de
lock
- LDM 2023-10-16 : triage dans l'ensemble de travail pour .NET 9
- LDM 2023-12-04 : modèle général rejeté, uniquement la casse spéciale du type
Lock
a été acceptée + ajout d’avertissements d’analyse statique
C# feature specifications