Bagaimana cara kerja kunci sebenarnya?


527

Saya melihat bahwa untuk menggunakan objek yang tidak aman, kami membungkus kode dengan kunci seperti ini:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Jadi apa yang terjadi ketika beberapa utas mengakses kode yang sama (mari kita asumsikan bahwa itu berjalan dalam aplikasi web ASP.NET). Apakah mereka antri? Jika demikian, berapa lama mereka akan menunggu?

Apa dampak kinerja karena menggunakan kunci?


Jawaban:


448

The lockPernyataan diterjemahkan oleh C # 3.0 dengan berikut ini:

var temp = obj;

Monitor.Enter(temp);

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

Di C # 4.0 ini telah berubah dan sekarang dihasilkan sebagai berikut:

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

Anda dapat menemukan lebih banyak info tentang apa yang Monitor.Enterada di sini . Mengutip MSDN:

Gunakan Enteruntuk mendapatkan Monitor pada objek yang dilewatkan sebagai parameter. Jika utas lain telah mengeksekusi Enter pada objek tetapi belum mengeksekusi yang sesuai Exit, utas saat ini akan memblokir sampai utas lainnya melepaskan objek. Adalah sah untuk utas yang sama meminta Enterlebih dari sekali tanpa memblokir; namun, jumlah Exitpanggilan yang sama harus dipanggil sebelum utas lainnya yang menunggu objek akan terbuka.

The Monitor.EnterMetode akan menunggu jauh; itu akan tidak waktu keluar.


15
Menurut MSDN "Menggunakan kata kunci kunci (C #) atau SyncLock (Visual Basic) umumnya lebih disukai daripada menggunakan kelas Monitor secara langsung, baik karena kunci atau SyncLock lebih ringkas, dan karena kunci atau SyncLock memastikan bahwa monitor yang mendasarinya dilepaskan, bahkan jika kode yang diproteksi mengeluarkan pengecualian. Ini dilakukan dengan kata kunci akhirnya, yang mengeksekusi blok kode yang terkait terlepas dari apakah pengecualian dilemparkan. " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
Apa gunanya var temp = obj; baris. karena ini hanya referensi awal, apa gunanya membuat yang lain?
priehl

11
@priehl Memungkinkan pengguna untuk mengubah objtanpa seluruh sistem menemui jalan buntu.
Steven

7
@Joymon akhirnya, setiap fitur bahasa adalah gula sintaksis. Fitur bahasa adalah tentang membuat pengembang lebih produktif dan membuat aplikasi lebih dapat dipelihara, seperti halnya fitur kunci juga.
Steven

2
Benar. Ini adalah keseluruhan tujuan dari lockpernyataan dan Monitor: sehingga Anda dapat melakukan operasi dalam satu utas tanpa harus khawatir tentang utas lain yang mengatasinya.
Dizzy H. Muffin

285

Lebih sederhana dari yang Anda pikirkan.

Menurut Microsoft : Kata lockkunci memastikan bahwa satu utas tidak memasukkan bagian kode yang penting sementara utas lainnya ada di bagian kritis. Jika utas lain mencoba memasukkan kode yang dikunci, ia akan menunggu, memblokir, hingga objek dilepaskan.

Kata lockkunci panggilan Enterdi awal blok dan Exitdi akhir blok. lockkata kunci sebenarnya menangani Monitorkelas di bagian belakang.

Sebagai contoh:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

Dalam kode di atas, pertama utas memasuki bagian kritis, dan kemudian akan mengunci obj. Ketika utas lain mencoba masuk, ia juga akan mencoba untuk mengunci obj, yang sudah dikunci oleh utas pertama. Utas kedua harus menunggu utas pertama dirilis obj. Ketika utas pertama pergi, maka utas lain akan mengunci objdan akan memasuki bagian kritis.


9
haruskah kita membuat objek tiruan untuk mengunci atau bisakah kita mengunci variabel yang ada dalam konteks?
batmaci

9
@batmaci - Mengunci objek boneka pribadi yang terpisah memberi Anda jaminan bahwa tidak ada orang lain yang mengunci objek itu. Jika Anda mengunci data dan potongan data yang sama terlihat di luar Anda kehilangan jaminan itu.
Umar Abbas

8
Apa yang terjadi jika lebih dari satu proses menunggu kunci dilepaskan? Apakah proses antrian menunggu sehingga mereka akan mengunci bagian penting dalam urutan FIFO?
jstuardo

@ jstuardo - Mereka mengantri, tetapi pesanan tidak dijamin sebagai FIFO. Lihatlah tautan ini: albahari.com/threading/part2.aspx
Umar Abbas


47

Tidak, mereka tidak mengantri, mereka sedang tidur

Pernyataan kunci formulir

lock (x) ... 

di mana x adalah ekspresi dari tipe referensi, tepat setara dengan

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

Anda hanya perlu tahu bahwa mereka sedang menunggu satu sama lain, dan hanya satu utas akan masuk untuk mengunci blok, yang lain akan menunggu ...

Monitor ditulis sepenuhnya dalam .net sehingga cukup cepat, lihat juga Monitor kelas dengan reflektor untuk lebih jelasnya


6
Perhatikan bahwa kode dipancarkan untuk lockpernyataan berubah sedikit di C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/...
LukeH

@ArsenMkrt, mereka tidak disimpan dalam antrian "Blocked" state ". Saya pikir ada beberapa perbedaan antara Sleep dan block state, bukan?
Mohanavel

apa maksudmu @Mohanavel?
Arsen Mkrtchyan

1
Bukan itu pertanyaannya. Pertanyaannya adalah tentang kata kunci "kunci". Misalkan proses memasuki bagian "kunci". Itu berarti bahwa proses memblokir potongan kode itu dan tidak ada proses lain yang dapat memasuki bagian itu sampai kunci itu dilepaskan. Nah .... sekarang, 2 proses lagi mencoba memasuki blok yang sama. Karena dilindungi oleh kata kunci "kunci", mereka akan menunggu, sesuai dengan apa yang dikatakan di forum ini. Saat proses pertama melepaskan kunci. Proses apa yang memasuki blok? yang pertama yang mencoba masuk atau yang terakhir?
jstuardo

1
Saya kira maksud Anda utas bukannya proses ... jika demikian, daripada jawabannya adalah Tidak, tidak ada jaminan yang akan masuk ... selengkapnya di sini stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan

29

Kunci akan memblokir utas lainnya dari mengeksekusi kode yang terkandung dalam blok kunci. Utas harus menunggu sampai utas di dalam blok kunci selesai dan kunci dilepaskan. Ini memang memiliki dampak negatif pada kinerja di lingkungan multithreaded. Jika Anda perlu melakukan ini, Anda harus memastikan kode di dalam blok kunci dapat diproses dengan sangat cepat. Anda harus mencoba menghindari kegiatan mahal seperti mengakses database dll.


11

Dampak kinerja tergantung pada cara Anda mengunci. Anda dapat menemukan daftar optimisasi yang baik di sini: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Pada dasarnya Anda harus mencoba untuk mengunci sesedikit mungkin, karena itu membuat kode tunggu Anda tertidur. Jika Anda memiliki beberapa perhitungan berat atau kode tahan lama (mis. Unggahan file) di kunci itu mengakibatkan hilangnya kinerja yang sangat besar.


1
Tetapi mencoba untuk menulis kode kunci-rendah sering dapat menghasilkan bug yang halus, sulit ditemukan dan diperbaiki, bahkan jika Anda seorang ahli di bidangnya. Menggunakan kunci sering kali lebih kecil dari dua kejahatan. Anda harus mengunci persis sebanyak yang Anda butuhkan, tidak lebih, tidak kurang!
LukeH

1
@LukeH: Ada beberapa pola penggunaan di mana kode low-lock bisa sangat sederhana dan mudah [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. Bahaya terbesar adalah bahwa jika persyaratan berkembang melampaui apa yang bisa ditangani oleh teknik tersebut, mungkin sulit untuk mengadaptasi kode untuk mengatasinya.
supercat

Tautan rusak.
CarenRose

8

Bagian dalam pernyataan kunci hanya dapat dieksekusi oleh satu utas, jadi semua utas lainnya akan menunggu tanpa batas hingga utas yang menahan kunci untuk menyelesaikannya. Ini dapat menyebabkan kebuntuan.


8

The lockpernyataan diterjemahkan ke panggilan ke Enterdan Exitmetode Monitor.

The lockpernyataan akan menunggu selamanya untuk objek mengunci akan dirilis.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.