Monitör vs kilit


90

C # 'da iş parçacığı güvenliği için Monitorsınıfın veya lockanahtar sözcüğün kullanılması ne zaman uygundur ?

DÜZENLEME: Şimdiye kadar verilen cevaplardan lock, Monitorsınıfa yapılan bir dizi çağrı için kısa bir el gibi görünüyor . Kilit çağrısı tam olarak ne içindir? Veya daha açık bir şekilde,

class LockVsMonitor
{
    private readonly object LockObject = new object();
    public void DoThreadSafeSomethingWithLock(Action action)
    {
        lock (LockObject)
        {
            action.Invoke();
        }
    }
    public void DoThreadSafeSomethingWithMonitor(Action action)
    {
        // What goes here ?
    }
}

Güncelleme

Hepinize yardımınız için teşekkür ederim: Sağladığınız bilgilerden bazılarının devamı olarak başka bir soru gönderdim. Bu alanda çok bilgili göründüğünüz için, bağlantıyı gönderdim: Kilitli istisnaları kilitlemek ve yönetmek için bu çözümde yanlış olan nedir?

Yanıtlar:


90

Eric Lippert blogunda bundan bahsediyor: Kilitler ve istisnalar karışmaz

Eşdeğer kod, C # 4.0 ve önceki sürümler arasında farklılık gösterir.


C # 4.0'da:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    { body }
}
finally
{
    if (lockWasTaken) Monitor.Exit(temp);
}

Monitor.EnterKilit alındığında bayrağı atomik olarak ayarlamaya dayanır .


Ve daha önce şuydu:

var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}

Bu Monitor.Enter, ile try. Hata ayıklama kodunda bu koşulun ihlal edildiğini düşünüyorum çünkü derleyici aralarına bir NOP ekledi ve böylece bunlar arasında iş parçacığı kürtajı mümkün oldu.


Bahsettiğim gibi, ilk örnek C # 4, diğeri ise önceki sürümlerin kullandığı şey.
CodesInChaos

Bir yan not olarak, CLR yoluyla C #, kilit anahtar kelimesinin bir uyarısından bahseder: kilidi serbest bırakmadan önce genellikle bozuk durumu (varsa) geri yüklemek için bir şeyler yapmak isteyebilirsiniz. Lock anahtar sözcüğü bir şeyleri catch bloğuna koymamıza izin vermediğinden, önemsiz olmayan rutinler için uzun sürüm try-catch-nihayet yazmayı düşünmeliyiz.
kizzx2

5
Paylaşılan durumu geri yükleyen IMO, kilitleme / çoklu iş parçacığı için ortogonaldir. Bu yüzden lockbloğun içinde bir try-catch / nihayet yapılmalıdır .
CodesInChaos

2
@ kizzx2: Böyle bir model, özellikle okuyucu-yazar kilitleri için güzel olurdu. Okuyucu kilidini tutan kod içinde bir istisna meydana gelirse, korunan kaynağın zarar görmesini beklemek için hiçbir neden yoktur ve bu nedenle onu geçersiz kılmak için bir neden yoktur. Bir yazıcı kilidinde bir istisna oluşursa ve istisna işleme kodu, korunan nesnenin durumunun onarıldığını açıkça belirtmiyorsa, bu, nesnenin zarar görebileceğini ve geçersiz kılınması gerektiğini gösterir. IMHO, beklenmedik istisnalar bir programı çökertmemeli, ancak bozuk olabilecek her şeyi geçersiz kılmalıdır.
supercat

2
@ArsenZahray PulseBasit bir kilitlemeye ihtiyacınız yok . Bazı gelişmiş çoklu iş parçacığı senaryolarında önemlidir. Asla Pulsedoğrudan kullanmadım.
CodesInChaos

43

locksadece + ve Monitor.Enterile kısayol . Yeterli olduğunda kilit ifadesini kullanın - TryEnter gibi bir şeye ihtiyacınız varsa, Monitörü kullanmanız gerekecektir.tryfinallyMonitor.Exit


23

Bir kilit ifadesi şuna eşdeğerdir:

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

Bununla birlikte, Monitor'ün ayrıca karmaşık çoklu okuma durumlarında genellikle yararlı olan Wait () ve Pulse () de yapabileceğini unutmayın .

Güncelleme

Ancak C # 4'te farklı şekilde uygulanır:

bool lockWasTaken = false;
var temp = obj;
try 
{
     Monitor.Enter(temp, ref lockWasTaken); 
     //your code
}
finally 
{ 
     if (lockWasTaken) 
             Monitor.Exit(temp); 
} 

Yorumlar ve bağlantılar için CodeInChaos'a teşekkürler


C # 4'te kilit ifadesi farklı şekilde uygulanır. blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
CodesInChaos

16

Monitordaha esnektir. Monitörü kullanmakla ilgili en sevdiğim durum, sıranızı beklemek istemediğinizde ve şunları atlamanızdır :

//already executing? forget it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}

6

Başkalarının dediği gibi lock, "eşdeğerdir"

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

Ancak meraktan lockdolayı, ona geçtiğiniz ilk referansı koruyacak ve değiştirirseniz atmayacaktır. Kilitli nesneyi değiştirmenin tavsiye edilmediğini ve bunu yapmak istemediğini biliyorum.

Ama yine bilim için bu gayet iyi çalışıyor:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        lock (lockObject)
        {
            lockObject += "x";
        }
    }));
Task.WaitAll(tasks.ToArray());

... Ve bu değil:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        Monitor.Enter(lockObject);
        try
        {
            lockObject += "x";
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }));
Task.WaitAll(tasks.ToArray());

Hata:

70783sTUDIES.exe'de 'System.Threading.SynchronizationLockException' türünde bir istisna oluştu ancak kullanıcı kodunda işlenmedi

Ek bilgi: Nesne senkronizasyon yöntemi, senkronize edilmemiş bir kod bloğundan çağrıldı.

Bunun nedeni , değişmez olduğu için değişmiş olana göre Monitor.Exit(lockObject);hareket edecek , o zaman onu senkronize edilmemiş bir kod bloğundan çağırıyorsunuz .. ama yine de. Bu sadece eğlenceli bir gerçek.lockObjectstrings


"Bunun nedeni, Monitor.Exit (lockObject); lockObject üzerinde hareket edecek". O zaman kilit nesneyle hiçbir şey yapmaz mı? Kilit nasıl çalışır?
Yugo Amaryl

@YugoAmaryl, ben kilit deyimi akla ilk geçti referans tutar yüzünden olduğunu varsayalım ve daha sonra benzeri değişti referansı kullanarak yerine kullanın:object temp = lockObject; Monitor.Enter(temp); <...locked code...> Monitor.Exit(temp);
Zhuravlev A.

3

İkisi de aynı şey. kilit c keskin anahtar kelimedir ve Monitor sınıfını kullanın.

http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx



1
temelde kilit uygulaması Monitor kullanır, ancak bunlar aynı şey değildir, monitör tarafından sağlanan ve kilit için mevcut olmayan yöntemleri ve ayrı kod bloklarında kilitleme ve kilidi açma yöntemlerini göz önünde bulundurun.
eran otzap

3

Monitörün kilidi ve temel davranışı (giriş + çıkış) aşağı yukarı aynıdır, ancak monitörün size daha fazla senkronizasyon olanağı sağlayan daha fazla seçeneği vardır.

Kilit bir kısayoldur ve temel kullanım seçeneğidir.

Daha fazla kontrole ihtiyacınız varsa, monitör daha iyi bir seçenektir. Gelişmiş kullanımlar (bariyerler, semaforlar vb.) İçin Wait, TryEnter ve Pulse'u kullanabilirsiniz.


1

Lock Lock anahtar sözcüğü, bir iş parçacığının bir kerede bir kod parçasını yürütmesini sağlar.

lock (lockObject)

        {
        //   Body
        }

Lock anahtar sözcüğü, belirli bir nesne için karşılıklı dışlama kilidini elde ederek, bir ifadeyi çalıştırarak ve ardından kilidi serbest bırakarak bir ifade bloğunu kritik bir bölüm olarak işaretler.

Başka bir iş parçacığı kilitli bir kod girmeye çalışırsa, nesne serbest bırakılıncaya kadar bekleyecektir.

Monitor Monitor, statik bir sınıftır ve System.Threading ad alanına aittir.

Herhangi bir zamanda kritik bölüme yalnızca bir iş parçacığının girebilmesi için nesne üzerinde özel kilit sağlar.

C # 'da monitör ve kilit arasındaki fark

Kilit, Monitörün kısayoludur. Try ve nihayet girin. Kilit kolları deneyin ve sonunda dahili olarak engelleyin Kilit = İzle + en sonunda deneyin.

Eğer daha fazla kontrol kullanarak gelişmiş çoklu çözümler uygulamak istiyorsanız TryEnter() Wait(), Pulse()ve PulseAll()yöntemleri, ardından Monitör sınıfı sizin seçenektir.

C # Monitor.wait(): Bir iş parçacığı, diğer iş parçacıklarının bildirmesini beklemektedir.

Monitor.pulse(): Bir iş parçacığı başka bir iş parçacığına bildirir.

Monitor.pulseAll(): Bir iş parçacığı, bir süreç içindeki diğer tüm iş parçacıklarını bilgilendirir


0

Yukarıdaki tüm açıklamalara ek olarak, kilit bir C # ifadesidir, oysa Monitor System.Threading ad alanında bulunan bir .NET sınıfıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.