Kilitli bir nesne, içinde bir istisna olursa kilitli kalır mı?


87

Ac # threading uygulamasında, bir nesneyi kilitlemem gerekirse, bir sıra diyelim ve bir istisna olursa nesne kilitli kalır mı? İşte sözde kod:

int ii;
lock(MyQueue)
{
   MyClass LclClass = (MyClass)MyQueue.Dequeue();
   try
   {
      ii = int.parse(LclClass.SomeString);
   }
   catch
   {
     MessageBox.Show("Error parsing string");
   }
}

Anladığım kadarıyla, yakalamadan sonraki kod çalışmıyor - ancak kilidin serbest bırakılıp bırakılmayacağını merak ediyordum.


1
Son bir düşünce olarak (güncellemelere bakın) - muhtemelen sadece kuyruktan çıkarma süresi boyunca kilidi tutmalısınız ... işlemi kilidin dışında yapın .
Marc Gravell

1
İstisna işlendiği için yakalamadan sonraki kod yürütülüyor
cjk

Teşekkürler Bunu kaçırmış olmalıyım, bu soruyu silmeli miyim?
Vort3x

4
Öyle görünüyor ki örnek kod bu soru için iyi değil, ancak soru oldukça geçerli.
SalvadorGomez

Yazan C # Designer - Lock & Exception
Jivan 21

Yanıtlar:


91

İlk; TryParse'ı düşündünüz mü?

in li;
if(int.TryParse(LclClass.SomeString, out li)) {
    // li is now assigned
} else {
    // input string is dodgy
}

Kilit 2 nedenden dolayı serbest bırakılacaktır; ilk olarak, lockesasen:

Monitor.Enter(lockObj);
try {
  // ...
} finally {
    Monitor.Exit(lockObj);
}

İkinci; içteki istisnayı yakalar ve yeniden atmazsınız, böylece lockasla bir istisna görmez. Tabii ki, bir MessageBox süresince kilidi tutuyorsunuz, bu bir sorun olabilir.

Bu nedenle, en ölümcül felakete neden olan kurtarılamaz istisnalar dışında tümüyle yayınlanacak.


15
Triparse'nin farkındayım, ancak sorumla gerçekten alakalı değil. Bu, soruyu açıklamak için basit bir koddu - ayrıştırmayla ilgili gerçek bir endişe değildi. Lütfen ayrıştırmayı , yakalamayı zorlayacak ve sizi rahat ettirecek herhangi bir kodla değiştirin .
Khadaji

13
Yeni İstisna oluşturmaya ne dersiniz ("açıklama amacıyla"); ;-p
Marc Gravell

1
Ve : blogs.msdn.com/ericlippert/archive/2009/03/06/…TheadAbortExceptionMonitor.Entertry
Igal Tabachnik

2
Akıntıları geçmek gibi "ölümcül, kurtarılamaz istisnalar".
Phil Cooper

99

Bu eski soruya verdikleri yanıtlarda hiç kimsenin bir istisnaya kilitlenmenin inanılmaz derecede tehlikeli bir şey olduğundan bahsetmediğini not ediyorum . Evet, C # içindeki kilit deyimleri "nihayet" anlamsallığına sahiptir; kontrol normal veya anormal şekilde kilitten çıktığında, kilit serbest bırakılır. Hepiniz bundan iyi bir şeymiş gibi bahsediyorsunuz, ama bu kötü bir şey! Eğer işlenmeyen bir özel durum için atar kilitli bölgeyi varsa doğru şeyin daha kullanıcı verilerini tahrip hemen önce hastalıklı süreci sonlandırmak değil, kilit boşaltın ve gitmeye devam .

Şuna bakın: Diyelim ki kapısında kilit olan bir banyonuz ve dışarıda bekleyen bir sıra insan var. Banyodaki bir bomba patlayarak içerideki kişiyi öldürür. Sorunuz "bu durumda kilit otomatik olarak açılacak ve böylece sonraki kişi banyoya girebilecek mi?" Evet olacak. Bu iyi birşey değil. Orada bir bomba patladı ve birini öldürdü! Tesisat muhtemelen yıkılmıştır, ev artık yapısal olarak sağlam değildir ve orada başka bir bomba olabilir . Yapılacak doğru şey, herkesi olabildiğince çabuk dışarı çıkarmak ve tüm evi yıkmaktır.

Demek istediğim, bir düşünün: bir veri yapısından başka bir iş parçacığında mutasyona uğratılmadan okumak için bir kod bölgesini kilitlediyseniz ve bu veri yapısındaki bir şey bir istisna attıysa, büyük olasılıkla veri yapısı bozuk . Kullanıcı verileri artık karıştı; Bu noktada kullanıcı verilerini kaydetmeyi denemek istemezsiniz çünkü daha sonra bozuk verileri kaydediyorsunuz . Sadece süreci sonlandırın.

Aynı anda durumu okuyan başka bir iş parçacığı olmadan bir mutasyon gerçekleştirmek için bir kod bölgesini kilitlediyseniz ve mutasyon atarsa, o zaman veriler daha önce bozuk değilse, kesinlikle şimdi . İşte kilidin koruması gereken senaryo da tam olarak budur . Şimdi bu durumu okumayı bekleyen koda derhal bozuk duruma erişim verilecek ve muhtemelen kendisi çökecektir. Yine, yapılacak doğru şey süreci sonlandırmaktır.

Nasıl dilimlediğin önemli değil, bir kilidin içindeki istisna kötü haberdir . Sorulması gereken doğru soru "bir istisna durumunda kilidim temizlenecek mi?" Sorulması gereken doğru soru, "bir kilidin içinde hiçbir zaman bir istisna olmamasını nasıl sağlayabilirim? Ve eğer varsa, o zaman programımı, mutasyonların önceki iyi durumlara geri döndürülmesi için nasıl yapılandırırım?"


19
Bu sorun, IMO'yu kilitlemek için oldukça ortogonaldir. Beklenen bir istisna ile karşılaşırsanız, kilitler dahil her şeyi temizlemek istersiniz. Ve beklenmedik bir istisna ile karşılaşırsanız, kilitli veya kilitsiz bir sorununuz var demektir.
Kodlar

10
Yukarıda anlatılan durumun bir genelleme olduğunu düşünüyorum. Bazen istisnalar felaket olayları tanımlar. Bazen yapmazlar. Her biri bunları kodda farklı şekilde kullanır. Bir istisnanın, istisnai ancak felaket niteliğinde olmayan bir olayın sinyali olduğu tamamen geçerlidir - istisnalar varsayılırsa = felaket, süreç sonlandırma durumu çok özeldir. Bunun felaket bir olay olabileceği gerçeği, sorunun geçerliliğini ortadan kaldırmaz - aynı düşünce silsilesi sizi hiçbir zaman istisnayla başa
çıkmamaya sevk edebilir

1
@GerasimosR: Gerçekten. Vurgulanması gereken iki nokta. Öncelikle, zararsız olduğu belirlenene kadar istisnaların felaket olacağı varsayılmalıdır. İkincisi, kilitli bir bölgeden zararsız bir istisna alıyorsanız, kilitli bölge muhtemelen kötü bir şekilde tasarlanmıştır; muhtemelen kilidin içinde çok fazla iş yapıyor.
Eric Lippert

1
Kusura bakmayın, ama öyle görünüyor ki, Bay Lippert, kaputun altındaki kilit bloğunun C # tasarımının nihayet bir ifade kullanan kötü bir şey olduğunu söylüyorsunuz. Bunun yerine, kilide yakalanmayan bir kilit ifadesinde istisnası olan her uygulamayı tamamen çökertmeliyiz. Belki de söylediğin bu değil, ama eğer öyleyse, takımın senin (saflık nedenleriyle aşırılık!) Rotanı değil, gittikleri rotayı izlemekten gerçekten mutluyum. Kusura bakmayın.
Nicholas Petersen

2
Birleştirilemez ile ne demek istediğim net değilse: s ve d olmak üzere iki listeyi alan, s'yi kilitleyen, d'yi kilitleyen, s'den bir öğeyi kaldıran, öğeyi d'ye ekleyen, kilidini açan bir "transfer" yöntemimiz olduğunu varsayalım. d, s'nin kilidini açar. Yöntem yalnızca , başka biri Y'den X'e aktarma girişiminde bulunurken, hiç kimse X listesinden Y listesine aktarma girişiminde bulunmazsa doğrudur . Aktarım yönteminin doğruluğu, ondan daha büyük bir soruna doğru bir çözüm oluşturmanıza izin vermez, çünkü kilitler küresel durumun güvenli olmayan mutasyonlarıdır. Güvenli bir şekilde "transfer etmek" için programdaki her kilidi bilmeniz gerekir .
Eric Lippert

43

evet, bu düzgün bir şekilde yayınlanacak; Sonunda ile / lockgibi davranır , bu nedenle nasıl çıkarsanız çıkın serbest bırakılacaktır. Yan not olarak,tryfinallyMonitor.Exit(myLock)catch(... e) {throw e;} olarak, yığın izlemeye zarar vereceği için en iyi şekilde kaçınılmalıdır e; onu yakalamak için iyi değil hiç kullanımını: alternatif olarak veya throw;yerine throw e;yeniden atış yapar ki.

Gerçekten bilmek istiyorsanız, C # 4 / .NET 4'teki bir kilit:

{
    bool haveLock = false;
    try {
       Monitor.Enter(myLock, ref haveLock);
    } finally {
       if(haveLock) Monitor.Exit(myLock);
    }
} 

14

"Bir Monitor.Enter çağrısına bir kilit ifadesi derlenir ve ardından bir dene… nihayet engellenir.

Hem x86 hem de x64 için JIT kodu üretimi, bir Monitor.Enter çağrısı ve hemen ardından gelen bir try bloğu arasında iş parçacığı iptalinin gerçekleşmemesini sağlar. "

Alındığı yer: Bu site


1
Bunun doğru olmadığı en az bir durum vardır: 4.'den önceki .net sürümlerinde hata ayıklama modunda iş parçacığı kürtajı. Bunun nedeni, C # derleyicisinin Monitor.Enterve arasına bir NOP eklemesidir try, böylece "hemen takip eder" koşulu JIT ihlal edildi.
CodesInChaos

6

Kilidiniz düzgün bir şekilde açılacaktır. A şu şekilde lockdavranır:

try {
    Monitor.Enter(myLock);
    // ...
} finally {
    Monitor.Exit(myLock);
}

Ve finallybloktan nasıl ayrılırsanız çıkın, tryblokların yürütülmesi garantilidir .


Aslında, hiçbir kodun çalıştırılması "garantili" değildir (örneğin, güç kablosunu çekebilirsiniz) ve bu,
4.0'daki

1
@MarcGravell: Bu iki nokta hakkında iki dipnot koymayı düşündüm. Ve sonra bunun pek bir önemi olmadığını düşündüm :)
Ry-

1
@MarcGravel: Bence herkes, bir programcının üzerinde herhangi bir kontrolü olmadığı için bir 'fişi çek' durumundan
bahsetmediğini varsayıyor

5

Sadece Marc'ın mükemmel cevabına biraz eklemek için.

Bunun gibi durumlar, lockanahtar kelimenin varlığının tam sebebidir . Geliştiricilerin kilidin finallyblokta serbest bırakıldığından emin olmalarına yardımcı olur .

Eğer kullanım zorunda ediyorsanız Monitor.Enter/ Exitbir zaman aşımı desteklemek için örneğin size arama yapmak emin olmalısınız Monitor.Exitiçinde finallybir istisna durumunda kilidin doğru bırakılmasını sağlamak için bloğun.

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.