Lock
-object
Notitie
Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.
Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. De verschillen zijn vastgelegd in de relevante verslagen (Language Design Meeting).
Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.
Probleem met Champion: https://github.com/dotnet/csharplang/issues/7104
Samenvatting
Speciaal geval voor hoe System.Threading.Lock
samenwerkt met het lock
trefwoord (waarbij de EnterScope
methode achter de schermen wordt aangeroepen).
Voeg waar mogelijk statische analysewaarschuwingen toe om onbedoeld misbruik van het type te voorkomen.
Motivatie
.NET 9 introduceert een nieuw System.Threading.Lock
type als een beter alternatief voor bestaande monitor-gebaseerde vergrendeling.
De aanwezigheid van het lock
trefwoord in C# kan ertoe leiden dat ontwikkelaars denken dat ze het kunnen gebruiken met dit nieuwe type.
Dit zou niet worden vergrendeld volgens de semantiek van dit type, maar zou het als een gewoon object behandelen en gebruikmaken van monitor-gebaseerde vergrendeling.
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
Gedetailleerd ontwerp
Semantiek van de vergrendelingsinstructie (§13.13) wordt gewijzigd om een speciaal geval te maken voor het System.Threading.Lock
type.
Een
lock
verklaring van de vormlock (x) { ... }
- waarbij
x
een expressie van het typeSystem.Threading.Lock
is, precies gelijk is aan:enusing (x.EnterScope()) { ... }
System.Threading.Lock
moeten de volgende vorm hebben:namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }
- wanneer
x
een expressie is van een reference_type, precies gelijk is aan: [...]
Houd er rekening mee dat de shape mogelijk niet volledig is ingeschakeld (er zijn bijvoorbeeld geen fouten of waarschuwingen als het Lock
type niet is sealed
), maar de functie werkt mogelijk niet zoals verwacht (er zijn bijvoorbeeld geen waarschuwingen bij het converteren van Lock
naar een afgeleid type, omdat de functie ervan uitgaat dat er geen afgeleide typen zijn).
Daarnaast worden er nieuwe waarschuwingen toegevoegd aan impliciete verwijzingsconversies (§10.2.8) bij het upcasten van het System.Threading.Lock
type:
De impliciete verwijzingsconversies zijn:
- Van elke reference_type tot
object
endynamic
.
- een waarschuwing wordt gemeld wanneer de reference_type bekend is
System.Threading.Lock
.- Van elke class_type
S
tot een class_typeT
, mitsS
is afgeleid vanT
.
- Er wordt een waarschuwing gerapporteerd wanneer
S
bekend is alsSystem.Threading.Lock
.- Van elke class_type
S
naar een interface_typeT
, mitsS
T
implementeert.
- Er wordt een waarschuwing gerapporteerd wanneer het bekend is dat
S
System.Threading.Lock
is.- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
Houd er rekening mee dat deze waarschuwing optreedt, zelfs voor gelijkwaardige expliciete conversies.
De compiler vermijdt het rapporteren van de waarschuwing in sommige gevallen wanneer het exemplaar niet kan worden vergrendeld nadat het is geconverteerd naar object
:
- wanneer de conversie impliciet is en deel uitmaakt van een aanroep van een object gelijkheidsoperator.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
Als u de waarschuwing wilt negeren en toch vergrendeling op basis van monitor wilt gebruiken, kunt u
- de gebruikelijke middelen voor het onderdrukken van waarschuwingen (
#pragma warning disable
), - api's rechtstreeks
Monitor
, - indirect gieten zoals
object AsObject<T>(T l) => (object)l;
.
Alternatieven
Ondersteuning voor een algemeen patroon dat andere typen ook kunnen gebruiken om te communiceren met het
lock
trefwoord. Dit is een toekomstig werk dat kan worden geïmplementeerd wanneerref struct
s kunnen deelnemen aan generics. Besproken in LDM 2023-12-04.Om dubbelzinnigheid tussen de bestaande monitorvergrendeling en de nieuwe
Lock
(of patroon in de toekomst) te voorkomen, kunnen we het volgende doen:- Introduceer een nieuwe syntaxis in plaats van de bestaande
lock
-instructie opnieuw te gebruiken. - Vereisen dat de nieuwe vergrendelingstypen
struct
zijn (omdat de bestaandelock
waardetypen niet toestaan). Er kunnen problemen zijn met standaardconstructors en kopiëren als de structs luie initialisatie hebben.
- Introduceer een nieuwe syntaxis in plaats van de bestaande
Het gegenereerde code kan worden versterkt om bestand te zijn tegen het afbreken van threads (die zelf al verouderd zijn).
We kunnen ook waarschuwen wanneer
Lock
wordt doorgegeven als een typeparameter, omdat het vergrendelen van een typeparameter altijd gebruikmaakt van op monitor gebaseerde vergrendeling:M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }
Dit zou echter waarschuwingen veroorzaken bij het opslaan van
Lock
s in een lijst die ongewenst is:List<Lock> list = new(); list.Add(new Lock()); // would warn here
We kunnen statische analyses opnemen om het gebruik van
System.Threading.Lock
inusing
metawait
s te voorkomen. Dat wil zeggen, we kunnen een fout of een waarschuwing geven voor code zoalsusing (lockVar.EnterScope()) { await ... }
. Dit is momenteel niet nodig omdatLock.Scope
eenref struct
is, zodat code toch illegaal is. Als we echter ooitref struct
inasync
methoden hebben toegestaan ofLock.Scope
hebben gewijzigd om geenref struct
te zijn, zou deze analyse nuttig worden. (We moeten waarschijnlijk ook rekening houden met deze vergrendelingstypen die overeenkomen met het algemene patroon als deze in de toekomst worden geïmplementeerd. Hoewel er mogelijk een opt-outmechanisme moet zijn, omdat sommige vergrendelingstypen mogelijk kunnen worden gebruikt metawait
.) U kunt dit ook implementeren als een analyse die wordt verzonden als onderdeel van de runtime.We zouden de beperking dat waardetypen niet kunnen worden
lock
ed kunnen versoepelen.- voor het nieuwe
Lock
type (alleen nodig als het API-voorstel dit heeft gewijzigd vanclass
instruct
), - voor het algemene patroon waarbij elk type kan deelnemen wanneer dit in de toekomst wordt geïmplementeerd.
- voor het nieuwe
We kunnen de nieuwe
lock
inasync
methoden toestaan waarbijawait
niet in delock
wordt gebruikt.- Omdat
lock
momenteel wordt verlaagd totusing
met eenref struct
als de resource, resulteert dit in een compilatietijdfout. De tijdelijke oplossing is om delock
te extraheren in een aparte methode die geenasync
is. - In plaats van de
ref struct Scope
te gebruiken, kunnen weLock.Enter
enLock.Exit
methoden intry
/finally
verzenden. De methodeExit
moet echter worden gegooid wanneer deze wordt aangeroepen vanuit een andere thread danEnter
, waardoor deze een threadzoekactie bevat die wordt vermeden bij het gebruik van deScope
. - Het beste zou zijn om compilatie van
using
op eenref struct
in dezeasync
-methoden toe te staan, op voorwaarde dat er geenawait
in deusing
-hoofdtekst aanwezig is.
- Omdat
Ontwerpvergaderingen
-
LDM 2023-05-01: eerste beslissing om een
lock
patroon te ondersteunen - LDM 2023-10-16: ingedeeld in de werkset voor .NET 9
-
LDM 2023-12-04: het algemene patroon afgewezen, alleen het type
Lock
met speciale behuizing geaccepteerd en waarschuwingen voor statische analyse toevoegen
C# feature specifications