Aracılığıyla paylaş


Lock nesne

Not

Bu makale bir özellik belirtimidir. Belirtim, özelliğin tasarım belgesi olarak görev alır. Önerilen belirtim değişikliklerini ve özelliğin tasarımı ve geliştirilmesi sırasında gereken bilgileri içerir. Bu makaleler, önerilen belirtim değişiklikleri son haline getirilene ve geçerli ECMA belirtimine dahil edilene kadar yayımlanır.

Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar, ilgilidil tasarım toplantısı (LDM) notlarında yakalanır.

Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek için belirtimleri makalesinde bulabilirsiniz.

Şampiyon sorunu: https://github.com/dotnet/csharplang/issues/7104

Özet

System.Threading.Lock'in lock anahtar sözcüğüyle nasıl etkileşime girdiğini özel olarak ele al (arka planda EnterScope yönteminin çağrılması). Mümkün olduğunda türün yanlışlıkla yanlış kullanılmasını önlemek için statik analiz uyarıları ekleyin.

Motivasyon

.NET 9, mevcut monitör tabanlı kilitlemeye daha iyi bir alternatif olarak yeni bir System.Threading.Lock türü sunuyor. C# dilinde lock anahtar sözcüğü bulunması, geliştiricilerin bunu bu yeni türle kullanabileceklerini düşünmesine neden olabilir. Bunu yapmak bu türün semantiğine göre kilitlenmez, bunun yerine bunu başka bir nesne olarak ele alır ve monitör tabanlı kilitlemeyi kullanır.

namespace System.Threading
{
    public sealed class Lock
    {
        public void Enter();
        public void Exit();
        public Scope EnterScope();
    
        public ref struct Scope
        {
            public void Dispose();
        }
    }
}

Ayrıntılı tasarım

Kilitleme deyiminin semantiği (§13.13), System.Threading.Lock türü için özel bir duruma göre değiştirilmiştir.

lock şeklinde bir lock (x) { ... } deyimi

  1. x System.Threading.Locktüründe bir ifade olduğu tam olarak şunun eşdeğeridir:
    using (x.EnterScope())
    {
        ...
    }
    
    ve System.Threading.Lock aşağıdaki şekle sahip olmalıdır:
    namespace System.Threading
    {
        public sealed class Lock
        {
            public Scope EnterScope();
    
            public ref struct Scope
            {
                public void Dispose();
            }
        }
    }
    
  2. burada x bir reference_typeifadesidir, tam olarak şunun eşdeğeridir: [...]

Şeklin tam olarak denetlenmeyebileceğini unutmayın (örneğin, Lock türü sealeddeğilse hata veya uyarı olmaz), ancak özellik beklendiği gibi çalışmayabilir (örneğin, özellik türetilmiş tür olmadığını varsaydığından Lock türetilmiş bir türe dönüştürürken hiçbir uyarı olmaz).

Ayrıca, türü yukarı yayınlanırken örtük başvuru dönüştürmelerine (System.Threading.Lock) yeni uyarılar eklenir:

Örtük referans dönüşümleri şunlardır:

  • herhangi bir reference_type kadar object ve dynamic.
    • reference_typeSystem.Threading.Lockolduğu bilindiğinde uyarı verilir.
  • Herhangi bir class_typeS'dan herhangi bir class_typeT'e, STtüretilmiş olması şartıyla.
    • S System.Threading.Lockolduğu bilindiğinde bir uyarı bildirilir.
  • Herhangi bir class_typeS'den herhangi bir interface_typeT'ye, ST'yi uyguluyorsa.
    • S System.Threading.Lockolduğu bilindiğinde bir uyarı bildirilir.
  • [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here

Bu uyarının eşdeğer açık dönüştürmelerde bile oluştuğuna dikkat edin.

Derleyici, örnek object'a dönüştürüldükten sonra kilitlenemediğinde bazı durumlarda uyarıyı bildirmekten kaçınır.

  • dönüştürme örtük olduğunda ve nesne eşitliği işleci çağırmasının bir parçası olduğunda.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
    // ...

Uyarıdan kaçmak ve monitör tabanlı kilitleme kullanımını zorlamak için

  • genellikle kullanılan uyarı bastırma yöntemleri (#pragma warning disable),
  • Monitor API'leri doğrudan
  • object AsObject<T>(T l) => (object)l;gibi dolaylı atama.

Alternatif

  • Diğer türlerin de lock anahtar sözcüğüyle etkileşime geçmek için kullanabileceği genel bir deseni destekleyin. Bu, ref struct'ler jeneriklere katılabildiğinde uygulanabilecek gelecekteki bir çalışmadır. LDM 2023-12-04üzerinde tartışıldı.

  • Mevcut monitör tabanlı kilitleme ile yeni Lock (veya gelecekte desen) arasındaki belirsizliği önlemek için şunları yapabiliriz:

    • Mevcut lock deyimini yeniden kullanmak yerine, yeni bir söz dizimi tanıtın.
    • Yeni kilit türlerinin structolmasını gerektir (mevcut lock değer türlerine izin vermediğinden). Yapıların yavaş başlatması varsa, varsayılan oluşturucular ve kopyalama ile ilgili sorunlar olabilir.
  • Kod oluşturma, iş parçacığı iptallerine karşı sağlamlaştırılabilir (ki bunlar da artık kullanılmıyor).

  • Tür parametresi olarak Lock geçirildiğinde de uyarabiliriz çünkü tür parametresinde kilitleme her zaman monitör tabanlı kilitleme kullanır:

    M(new Lock()); // could warn here
    
    void M<T>(T x) // (specifying `where T : Lock` makes no difference)
    {
        lock (x) { } // because this uses Monitor
    }
    

    Ancak bu, Lock'lerin bir listede saklanması sırasında istenmeyen uyarılara neden olur.

    List<Lock> list = new();
    list.Add(new Lock()); // would warn here
    
  • System.Threading.Lockile using'lerde await kullanımını önlemek için statik analiz içerebiliriz. Örneğin, using (lockVar.EnterScope()) { await ... }gibi bir kod için ya bir hata ya da bir uyarı verebiliriz. Şu anda Lock.Scope bir ref structolduğundan bu gerekli değildir, bu nedenle kod yine de geçersizdir. Eğer bir gün ref struct yöntemlerinde async'lere izin verirsek veya Lock.Scope'yi ref structolmayacak şekilde değiştirirsek, bu analiz yararlı hale gelecektir. (Gelecekte uygulanırsa genel desenle eşleşen tüm kilit türlerini de göz önünde bulundurmamız gerekebilir. Bazı kilit türlerinin awaitile kullanılmasına izin verilebileceği için bir geri çevirme mekanizması olması gerekebilir.) Alternatif olarak, bu çalışma zamanının bir parçası olarak gönderilen bir çözümleyici olarak uygulanabilir.

  • Değer türlerinin lockyapılamama kısıtlamasını gevşetebiliriz

    • yeni Lock türü için (sadece API teklifi bunu class'den struct'ye değiştirdiyse gereklidir)
    • gelecekte uygulandığında herhangi bir türün katılabileceği genel düzen için.
  • Yeni lock, asynciçinde await kullanılmadığı lock yöntemlerinde izin verebiliriz.

    • Şu anda lock, using kaynağı ile ref struct'e düşürüldüğünden, bu da derleme sırasında bir hata oluşmasına neden olur. Geçici çözüm, lock'ı ayrı birasync olmayan metoda çıkarmaktır.
    • ref struct Scopekullanmak yerine, Lock.EnterLock.Exittryiçinde / ve finally yöntemleri yayabiliriz. Ancak, Exit yöntemi, Enter'den farklı bir iş parçacığından çağrıldığında mutlaka bir hata atmalıdır; bu nedenle Scopekullanıldığında kaçınılan bir iş parçacığı araması içerir.
    • En iyisi, using gövdesinde ref struct yoksa, async'ın await üzerindeki derlenmesine using yöntemlerinde izin vermektir.

Tasarım toplantıları

  • LDM 2023-05-01: lock deseni desteklemeye ilişkin ilk karar
  • LDM 2023-10-16: .NET 9 için çalışma kümesine tasnif edildi
  • LDM 2023-12-04: genel desen reddedildi, yalnızca Lock türüne özel işlem kabul edildi + statik analiz uyarıları eklendi.