Monitor vs kunci

89

Kapan waktu yang tepat untuk menggunakan Monitorkelas atau lockkata kunci untuk keamanan benang di C #?

EDIT: Sepertinya dari jawaban sejauh lockini yang singkat untuk serangkaian panggilan ke Monitorkelas. Untuk apa sebenarnya panggilan kunci itu? Atau lebih eksplisit,

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 ?
    }
}

Memperbarui

Terima kasih atas bantuan Anda: Saya telah memposting pertanyaan lain sebagai tindak lanjut dari beberapa informasi yang Anda semua berikan. Karena Anda tampaknya berpengalaman dalam bidang ini, saya telah memposting tautan: Apa yang salah dengan solusi untuk mengunci dan mengelola pengecualian yang terkunci ini?

orang pintar
sumber

Jawaban:

89

Eric Lippert membicarakan hal ini di blognya: Kunci dan pengecualian tidak tercampur

Kode yang setara berbeda antara C # 4.0 dan versi sebelumnya.


Di C # 4.0 itu adalah:

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

Ini bergantung pada Monitor.Enterpengaturan bendera secara atom saat kunci diambil.


Dan sebelumnya:

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

Ini bergantung pada tidak terkecuali dilemparkan antara Monitor.Enterdan try. Saya pikir dalam kode debug kondisi ini dilanggar karena kompilator memasukkan NOP di antara mereka dan dengan demikian membuat aborsi benang di antara mereka mungkin.

CodesInChaos
sumber
Seperti yang saya nyatakan, contoh pertama adalah C # 4 dan yang lainnya adalah apa yang digunakan versi sebelumnya.
CodesInChaos
Sebagai catatan tambahan, C # via CLR menyebutkan peringatan dari kata kunci kunci: Anda mungkin sering ingin melakukan sesuatu untuk memulihkan keadaan rusak (jika ada) sebelum melepaskan kunci. Karena kata kunci kunci tidak membiarkan kita meletakkan sesuatu di blok catch, kita harus mempertimbangkan untuk menulis versi panjang try-catch-akhirnya untuk rutinitas yang tidak sepele.
kizzx2
5
IMO memulihkan status bersama adalah ortogonal untuk penguncian / multi-threading. Jadi itu harus dilakukan dengan coba-tangkap / akhirnya di dalam lockblok.
CodesInChaos
2
@ kizzx2: Pola seperti itu akan sangat bagus dengan kunci pembaca-penulis. Jika pengecualian terjadi dalam kode yang menahan kunci pembaca, tidak ada alasan untuk mengharapkan bahwa sumber daya yang dijaga mungkin rusak, dan karenanya tidak ada alasan untuk membatalkannya. Jika pengecualian terjadi dalam kunci penulis dan kode penanganan pengecualian tidak secara jelas menunjukkan bahwa status objek yang dijaga telah diperbaiki, itu menunjukkan bahwa objek mungkin rusak dan harus dibatalkan. IMHO, pengecualian tak terduga seharusnya tidak merusak program, tetapi harus membatalkan apa pun yang mungkin rusak.
supercat
2
@ArsenZahray Anda tidak perlu Pulsemengunci sederhana. Ini penting dalam beberapa skenario multi-threading lanjutan. Saya tidak pernah menggunakan Pulsesecara langsung.
CodesInChaos
43

lockhanyalah pintasan untuk Monitor.Enterdengan try+ finallydan Monitor.Exit. Gunakan pernyataan kunci kapan pun itu cukup - jika Anda membutuhkan sesuatu seperti TryEnter, Anda harus menggunakan Monitor.

Lukáš Novotný
sumber
23

Pernyataan kunci setara dengan:

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

Namun, perlu diingat bahwa Monitor juga dapat Wait () dan Pulse () , yang sering berguna dalam situasi multithreading yang kompleks.

Memperbarui

Namun di C # 4 penerapannya berbeda:

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

Thanx ke CodeInChaos untuk komentar dan tautan

Shekhar_Pro
sumber
Dalam C # 4 pernyataan kunci diimplementasikan secara berbeda. blogs.msdn.com/b/ericlippert/archive/2009/03/06/...
CodesInChaos
14

Monitorlebih fleksibel. Kasus penggunaan favorit saya menggunakan monitor adalah ketika Anda tidak ingin menunggu giliran Anda dan lewati saja :

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

Seperti yang dikatakan orang lain, lock"setara" dengan

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

Tapi hanya karena ingin tahu, lockakan menyimpan referensi pertama yang Anda berikan padanya dan tidak akan dibuang jika Anda mengubahnya. Saya tahu tidak disarankan untuk mengubah objek yang terkunci dan Anda tidak ingin melakukannya.

Tetapi sekali lagi, untuk sains, ini berfungsi dengan baik:

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());

... Dan ini tidak:

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());

Kesalahan:

Pengecualian jenis 'System.Threading.SynchronizationLockException' terjadi di 70783sTUDIES.exe tetapi tidak ditangani dalam kode pengguna

Informasi tambahan: Metode sinkronisasi objek dipanggil dari blok kode yang tidak tersinkronisasi.

Ini karena Monitor.Exit(lockObject);akan bertindak atas lockObjectyang telah berubah karena stringstidak dapat diubah, lalu Anda memanggilnya dari blok kode yang tidak tersinkronisasi .. tetapi bagaimanapun juga. Ini hanya fakta yang menyenangkan.

André Pena
sumber
"Ini karena Monitor.Exit (lockObject); akan bekerja pada lockObject". Lalu kunci tidak melakukan apa-apa dengan objeknya? Bagaimana kunci bekerja?
Yugo Amaryl
@YugoAmaryl, saya kira itu karena pernyataan kunci diingat pertama kali melewati referensi dan kemudian menggunakannya daripada menggunakan referensi yang diubah, seperti:object temp = lockObject; Monitor.Enter(temp); <...locked code...> Monitor.Exit(temp);
Zhuravlev A.
3

Keduanya adalah hal yang sama. kunci adalah kata kunci yang tajam dan gunakan kelas Monitor.

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

RobertoBr
sumber
3
Lihat msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx "Sebenarnya, kata kunci kunci diimplementasikan dengan kelas Monitor. Misalnya"
RobertoBr
1
implementasi yang mendasari penguncian menggunakan Monitor tetapi mereka tidak sama, pertimbangkan metode yang disediakan oleh monitor yang tidak ada untuk penguncian, dan cara Anda dapat mengunci dan membuka kunci dalam blok kode yang terpisah.
eran otzap
3

Kunci dan perilaku dasar monitor (masuk + keluar) kurang lebih sama, tetapi monitor memiliki lebih banyak opsi yang memungkinkan Anda lebih banyak kemungkinan sinkronisasi.

Kuncinya adalah jalan pintas, dan ini adalah opsi untuk penggunaan dasar.

Jika Anda membutuhkan lebih banyak kontrol, monitor adalah pilihan yang lebih baik. Anda dapat menggunakan Wait, TryEnter dan Pulse, untuk penggunaan tingkat lanjut (seperti penghalang, semaphore, dan sebagainya).

Borja
sumber
1

Kata kunci Lock Lock memastikan bahwa satu utas mengeksekusi sepotong kode pada satu waktu.

kunci (lockObject)

        {
        //   Body
        }

Kata kunci kunci menandai blok pernyataan sebagai bagian penting dengan mendapatkan kunci saling-pengecualian untuk objek tertentu, menjalankan pernyataan dan kemudian melepaskan kunci

Jika utas lain mencoba memasukkan kode terkunci, utas akan menunggu, memblokir, hingga objek dilepaskan.

Monitor Monitor adalah kelas statis dan termasuk dalam namespace System.Threading.

Ini memberikan kunci eksklusif pada objek sehingga hanya satu utas yang dapat masuk ke bagian kritis pada titik waktu tertentu.

Perbedaan antara Monitor dan Lock in C #

Kunci adalah jalan pintas untuk Monitor. Masuk dengan mencoba dan akhirnya. Gagang kunci mencoba dan akhirnya memblokir secara internal Lock = Monitor + coba akhirnya.

Jika Anda ingin lebih kontrol untuk menerapkan solusi multithreading canggih menggunakan TryEnter() Wait(), Pulse()dan PulseAll()metode, maka kelas monitor adalah pilihan Anda.

C # Monitor.wait(): Sebuah utas menunggu utas lain untuk memberi tahu.

Monitor.pulse(): Sebuah utas memberi tahu utas lain.

Monitor.pulseAll(): Sebuah utas memberi tahu semua utas lainnya dalam suatu proses

Aatrey
sumber
0

Selain semua penjelasan di atas, kunci adalah pernyataan C # sedangkan Monitor adalah kelas .NET yang terletak di namespace System.Threading.

PureSilence
sumber