Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
Objet
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
lockdu formulairelock (x) { ... }
- où
xest une expression de typeSystem.Threading.Lock, équivaut précisément à :etusing (x.EnterScope()) { ... }System.Threading.Lockdoivent avoir la forme suivante :namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }- où
xest 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 à
objectetdynamic.
- 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 queSsoit dérivé deT.
- Un avertissement est signalé lorsque
Sest connu pour êtreSystem.Threading.Lock.- De n’importe quel class_type
Sà n’importe quel interface_typeT, à condition queSimplémenteT.
- Un avertissement est signalé lorsque
Sest 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
Monitordirectement, - 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 structpeuvent 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
lockexistante. - Exiger que les nouveaux types de verrous soient
structs (étant donné que lelockexistant 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
Lockest 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
Locks dans une liste qui n’est pas souhaitable :List<Lock> list = new(); list.Add(new Lock()); // would warn hereNous pourrions inclure une analyse statique pour empêcher l’utilisation de
System.Threading.Lockdansusings avecawaits. 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.Scopeest unref struct, de sorte que le code est illégal de toute façon. Toutefois, si jamais nous permettions desref structdans les méthodesasyncou changionsLock.Scopepour 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
lockdans les méthodesasyncoùawaitn’est pas utilisé à l'intérieur delock.- Actuellement, étant donné que
lockest réduit àusingavec unref structen tant que ressource, cela entraîne une erreur de compilation. La solution de contournement consiste à extraire lelockdans une méthode distincte nonasync. - Au lieu d’utiliser le
ref struct Scope, nous pourrions émettre des méthodesLock.EnteretLock.Exitdanstry/finally. Toutefois, la méthodeExitdoit 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
usingsur unref structdans les méthodesasyncs’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
Locka été acceptée + ajout d’avertissements d’analyse statique
C# feature specifications