Kilit tam olarak nasıl çalışır?


527

İş parçacığı güvenli olmayan nesneleri kullanmak için kodu böyle bir kilit ile sarın görüyorum:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Birden fazla iş parçacığı aynı koda eriştiğinde ne olur (bir ASP.NET web uygulamasında çalıştığını varsayalım). Kuyrukta mı? Eğer öyleyse ne kadar bekleyecekler?

Kilitlerin kullanılmasından dolayı performans etkisi nedir?


Yanıtlar:


448

lockDeyim aşağıdakine C # 3.0 ile çevrilmiştir:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

C # 4.0'da bu değişti ve şimdi aşağıdaki gibi üretildi:

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

Ne hakkında daha fazla bilgi bulabilirsiniz Monitor.Enteryapar burada . MSDN'e alıntı yapmak için:

EnterMonitörü parametre olarak iletilen nesne üzerinde almak için kullanın . Enter Nesnede başka bir iş parçacığı çalıştırılmış ancak karşılık gelen işlemi henüz yürütmemişse Exit, diğer iş parçacığı nesneyi serbest bırakana kadar geçerli iş parçacığı engellenir. Aynı iş parçacığının Enterengellemeden bir kereden fazla çağırması yasaldır ; ancak, Exitnesnede bekleyen diğer evrelerin engelini kaldırmadan önce eşit sayıda çağrı başlatılmalıdır.

Monitor.EnterYöntem sonsuz bekler; o olacak değil zaman aşımına.


15
MSDN'ye göre "Kilit (SyncLock) veya SyncLock (Visual Basic) anahtar kelimesinin kullanılması, doğrudan Monitor sınıfının kullanılmasına göre tercih edilir, çünkü hem kilit hem de SyncLock daha özlüdür ve kilit veya SyncLock, temel monitörün serbest bırakılmasını sağlar. korunan kod bir istisna atarsa. Bu, bir istisna atılıp atılmadığına bakılmaksızın ilişkili kod bloğunu yürüten nihayet anahtar sözcüğü ile gerçekleştirilir. " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
Var temp = obj'in anlamı nedir; hat. sadece başlangıç ​​için bir ref olduğundan, bir başkasını yapmak ne işe yarar?
priehl

11
@priehl Kullanıcının objtüm sistem olmadan kilitlenmesine izin vermesini sağlar .
Steven

7
@ Sonunda, her dil özelliği sözdizimsel şekerdir. Dil özellikleri, kilit özelliği gibi geliştiricileri daha verimli hale getirmek ve uygulamaları daha sürdürülebilir hale getirmekle ilgilidir.
Steven

2
Doğru. Bu lock-statement ve Monitor öğesinin tüm amacıdır : böylece bir iş parçacığını emen başka bir iş parçacığı hakkında endişelenmenize gerek kalmadan bir iş parçacığında bir işlem gerçekleştirebilirsiniz.
Dizzy H. Muffin

285

Düşündüğünüzden daha basit.

Microsoft'a göre : lockAnahtar kelime, bir iş parçacığının kritik bölümdeyken bir iş parçacığının kritik bir kod bölümüne girmemesini sağlar. Başka bir iş parçacığı kilitli bir kod girmeye çalışırsa, nesne serbest bırakılıncaya kadar bekler, engeller.

lockAnahtar kelime aramaları Enterbloğun başlangıcında ve Exitblok sonunda. lockanahtar kelime aslında Monitorarka uçtaki sınıfı işler .

Örneğin:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

Yukarıdaki kodda, önce iplik kritik bir bölüme girer ve sonra kilitlenir obj. Başka bir evre girmeye çalıştığında obj, ilk evre tarafından zaten kilitlenmiş olan kilitlenmeyi de dener . İkinci iş parçacığının ilk iş parçacığının serbest bırakılmasını beklemesi gerekir obj. İlk iş parçacığı ayrıldığında, başka bir iş parçacığı kilitlenir objve kritik bölüme girer.


9
kilitlemek için sahte bir nesne oluşturmalı mıyız yoksa bağlamda var olan bir değişkeni kilitleyebilir miyiz?
batmaci

9
@batmaci - Ayrı bir özel kukla nesneye kilitleme, o nesnede kimsenin kilitlenmediğini garanti eder. Verileri kilitlerseniz ve aynı veri parçası dışarıda görünürse bu garantiyi kaybedersiniz.
Umar Abbas

8
Kilidin serbest bırakılmasını birden fazla işlem bekliyorsa ne olur? Bekleme işlemleri, kritik bölümü FIFO sırasına göre kilitleyecek şekilde sıraya alınmış mı?
jstuardo

@jstuardo - Sıraya alındı, ancak siparişin FIFO olduğu garanti edilmiyor. Bu bağlantıya göz atın: albahari.com/threading/part2.aspx
Umar Abbas


47

Hayır, sıraya alınmadılar, uyuyorlar

Formun kilit ifadesi

lock (x) ... 

burada x, bir referans tipinin bir ifadesidir, tam olarak

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

Sadece birbirlerini beklediklerini bilmelisiniz ve kilit bloğuna sadece bir iplik girecek, diğerleri bekleyecek ...

O da bakmak, yeterince hızlı yani Monitör .net tamamen yazılır sınıf Monitor ile reflektör fazla ayrıntı için


6
Not için yayılan kod bu lockdeyimi C # 4'te biraz değişti: blogs.msdn.com/b/ericlippert/archive/2009/03/06/...
LukeH

@ArsenMkrt, onlar "Engellenen" devlet "Kuyrukta tutulur ?. Uyku ve blok devlet arasında bazı farklar olduğunu düşünüyorum, değil mi?
Mohanavel

Ne demek istiyorsun @Mohanavel?
Arsen Mkrtchyan

1
Soru bu değildi. Soru "kilitli" anahtar kelimeyle ilgiliydi. Bir işlemin "kilit" bölümüne girdiğini varsayalım. Bu, işlemin bu kod parçasını bloke ettiği ve bu kilit serbest bırakılıncaya kadar başka hiçbir işlemin bu bölüme giremeyeceği anlamına gelir. Şey ... şimdi, 2 blok daha aynı bloğa girmeye çalışıyor. "Kilit" anahtar kelimesi ile korunduğundan, bu forumda söylenenlere göre bekleyeceklerdir. İlk işlem kilidi serbest bıraktığında. Bloğa hangi süreç girer? girmeye çalışan ilk veya sonuncusu?
jstuardo

1
Sanırım süreç yerine iplik demek istiyorsun ... öyleyse, cevap Hayır, hangisinin gireceğine dair bir garanti yoktur ... more here stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan

29

Kilitler, diğer iş parçacıklarının kilit bloğunda bulunan kodu yürütmesini engeller. İpliklerin, kilit bloğunun içindeki iplik tamamlanana ve kilit serbest kalana kadar beklenmesi gerekecektir. Bunun çok iş parçacıklı bir ortamda performans üzerinde olumsuz bir etkisi vardır. Bunu yapmanız gerekiyorsa, kilit bloğundaki kodun çok hızlı bir şekilde işlenebildiğinden emin olmalısınız. Veritabanına erişim gibi pahalı etkinliklerden kaçınmaya çalışmalısınız.


11

Performans etkisi kilitleme yönteminize bağlıdır. Optimizasyonların iyi bir listesini burada bulabilirsiniz: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Temel olarak, bekleme kodunuzu uyku moduna geçirdiği için mümkün olduğunca az kilitlemeye çalışmalısınız. Bir kilitte bazı ağır hesaplarınız veya uzun süreli kodunuz varsa (örn. Dosya yükleme) büyük bir performans kaybına neden olur.


1
Ancak düşük kilitli kod yazmaya çalışmak, alanında uzman olsanız bile, genellikle küçük, bulunması ve düzeltilmesi zor hatalara neden olabilir. Bir kilit kullanmak genellikle iki kötülükten daha azdır. Tam olarak ihtiyacınız olduğu kadar kilitlemelisiniz, daha fazla, daha az değil!
LukeH

1
@LukeH: Düşük kilit kodunun çok basit ve kolay olabileceği bazı kullanım şekilleri vardır [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. En büyük tehlike, eğer gereksinimler bu tür tekniklerin ele alınabileceğinin ötesine geçerse, kodu bununla başa çıkmak için uyarlamak zor olabilir.
supercat

Bağlantı koptu.
CarenRose

8

Lock deyimi içindeki parça yalnızca bir iş parçacığı tarafından yürütülebilir, böylece diğer tüm iş parçacıkları süresiz olarak kilidi tutan iş parçacığının bitmesini bekler. Bu sözde kilitlenme ile sonuçlanabilir.


8

lockİfadesi için çağrılara çevrilmiştir Enterve Exityöntemleri Monitor.

lockKilitleme nesnesi serbest olması için deyim süresiz bekleyecektir.


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.