Apa perbedaan antara berbagai opsi sinkronisasi threading di C #?


Jawaban:


135

Pertanyaan yang bagus Saya mungkin salah .. Biarkan saya mencoba .. Revisi # 2 dari jawaban asal saya .. dengan sedikit lebih banyak pengertian. Terima kasih telah membuat saya membaca :)

mengunci (obj)

  • adalah CLR membangun yang untuk sinkronisasi thread (intra-objek?). Memastikan bahwa hanya satu utas yang dapat mengambil kepemilikan kunci objek & memasukkan blok kode yang dikunci. Utas lain harus menunggu hingga pemilik saat ini melepaskan kunci dengan keluar dari blok kode. Juga disarankan agar Anda mengunci objek anggota pribadi kelas Anda.

Monitor

  • kunci (obj) diterapkan secara internal menggunakan Monitor. Anda harus memilih kunci (obj) karena itu mencegah Anda melakukan kesalahan seperti melupakan prosedur pembersihan. Ini bukti bodoh yang merupakan konstruksi Monitor jika Anda mau.
    Menggunakan Monitor umumnya lebih disukai daripada mutex, karena monitor dirancang khusus untuk .NET Framework dan karenanya memanfaatkan sumber daya dengan lebih baik.

Menggunakan kunci atau monitor berguna untuk mencegah eksekusi simultan blok kode yang sensitif terhadap benang, tetapi konstruksi ini tidak memungkinkan satu utas untuk mengomunikasikan suatu peristiwa ke yang lain. Ini memerlukan peristiwa sinkronisasi , yang merupakan objek yang memiliki salah satu dari dua status, yang diberi sinyal dan yang tidak ditandai, yang dapat digunakan untuk mengaktifkan dan menangguhkan utas. Mutex, Semaphores adalah konsep tingkat OS. mis. dengan nama mutex Anda dapat menyinkronkan banyak (ex) terkelola (memastikan bahwa hanya satu contoh aplikasi Anda berjalan pada mesin.)

Mutex:

  • Namun, tidak seperti monitor, mutex dapat digunakan untuk menyinkronkan utas lintas proses. Ketika digunakan untuk sinkronisasi antar-proses, suatu mutex disebut mutex yang dinamai karena ia akan digunakan dalam aplikasi lain, dan karena itu ia tidak dapat dibagi dengan menggunakan variabel global atau statis. Itu harus diberi nama sehingga kedua aplikasi dapat mengakses objek mutex yang sama. Sebaliknya, kelas Mutex adalah pembungkus untuk konstruksi Win32. Meskipun lebih kuat daripada monitor, mutex membutuhkan transisi interop yang lebih mahal secara komputasi daripada yang dibutuhkan oleh kelas Monitor.

Semaphores (melukai otak saya).

  • Gunakan kelas Semaphore untuk mengontrol akses ke kumpulan sumber daya. Thread masuk ke semaphore dengan memanggil metode WaitOne, yang diwarisi dari kelas WaitHandle, dan lepaskan semaphore dengan memanggil metode Release. Hitungan pada semaphore dikurangi setiap kali utas memasuki semaphore, dan bertambah ketika utas melepaskan semaphore. Ketika hitungannya nol, permintaan selanjutnya memblokir sampai utas lainnya melepaskan semaphore. Ketika semua utas telah merilis semaphore, hitungannya adalah pada nilai maksimum yang ditentukan ketika semaphore dibuat. Sebuah utas dapat memasuki semaphore beberapa kali .. Kelas Semaphore tidak memberlakukan identitas utas pada WaitOne atau Release .. programmer bertanggung jawab untuk tidak mengacaukan. Semaphores terdiri dari dua jenis: semaphores lokal dan dinamaisistem semaphores. Jika Anda membuat objek Semaphore menggunakan konstruktor yang menerima nama, itu terkait dengan semafor sistem operasi dari nama itu. Semafor sistem yang dinamai terlihat di seluruh sistem operasi, dan dapat digunakan untuk menyinkronkan kegiatan proses. Semafor lokal hanya ada dalam proses Anda. Ini dapat digunakan oleh utas apa pun dalam proses Anda yang memiliki referensi ke objek Semaphore lokal. Setiap objek Semaphore adalah semaphore lokal yang terpisah.

HALAMAN UNTUK BACA - Sinkronisasi Thread (C #)


18
Anda mengklaim bahwa Monitorkomunikasi tidak dibiarkan salah; Anda masih dapat Pulsedll denganMonitor
Marc Gravell

3
Lihatlah deskripsi alternatif Semaphores - stackoverflow.com/a/40473/968003 . Pikirkan semaphores sebagai penjaga di klub malam. Ada sejumlah orang yang berdedikasi yang diperbolehkan di klub sekaligus. Jika klub penuh tidak ada yang diizinkan untuk masuk, tetapi begitu satu orang meninggalkan orang lain mungkin masuk.
Alex Klaus

31

Re "Menggunakan kelas sinkronisasi .Net lainnya" - beberapa yang lain yang harus Anda ketahui:

Ada juga lebih banyak konstruksi penguncian (overhead rendah) di CCR / TPL ( CTP Parallel Extensions ) - tetapi IIRC, ini akan tersedia di .NET 4.0


Jadi jika saya ingin komunikasi sinyal sederhana (katakanlah penyelesaian operasi async) - saya harus Monitor.Pulse? atau gunakan SemaphoreSlim atau TaskCompletionSource?
Vivek

Gunakan TaskCompletionSource untuk operasi async. Pada dasarnya, berhentilah memikirkan utas dan mulailah memikirkan tugas (unit kerja). Utas adalah detail implementasi dan tidak relevan. Dengan mengembalikan TCS, Anda dapat mengembalikan hasil, kesalahan atau menangani pembatalan dan mudah dikompilasi dengan operasi async lainnya (seperti async menunggu atau ContinueWith).
Simon Gillbee

14

Seperti yang dinyatakan dalam ECMA, dan seperti yang dapat Anda amati dari metode Reflected, pernyataan kunci pada dasarnya setara dengan

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
   
}
finally {
   System.Threading.Monitor.Exit(obj);
}

Dari contoh di atas kita melihat bahwa Monitor dapat mengunci objek.

Mutexe berguna ketika Anda membutuhkan sinkronisasi antar proses karena mereka dapat mengunci pengenal string. Pengidentifikasi string yang sama dapat digunakan oleh berbagai proses untuk mendapatkan kunci.

Semaphore seperti Mutex pada steroid, mereka memungkinkan akses bersamaan dengan memberikan jumlah maksimum akses bersamaan '. Setelah batas tercapai, semaphore mulai memblokir akses lebih lanjut ke sumber daya sampai salah satu penelepon melepaskan semaphore.


5
Gula sintaksis ini telah sedikit berubah di C # 4 Periksa blogs.msdn.com/ericlippert/archive/2009/03/06/…
Peter Gfader

14

Saya melakukan dukungan kelas & CLR untuk threading di DotGNU dan saya punya beberapa pemikiran ...

Kecuali Anda membutuhkan kunci proses silang, Anda harus selalu menghindari penggunaan Mutex & Semaphores. Kelas-kelas ini dalam. NET adalah pembungkus di sekitar Win32 Mutex dan Semaphores dan agak berat (mereka membutuhkan konteks switch ke Kernel yang mahal - terutama jika kunci Anda tidak dalam pertikaian).

Seperti yang lain disebutkan, pernyataan kunci C # adalah sihir kompiler untuk Monitor.Enter dan Monitor.Exit (ada dalam coba / akhirnya).

Monitor memiliki mekanisme sinyal / tunggu sederhana namun kuat yang tidak dimiliki Mutex melalui metode Monitor.Pulse / Monitor.Wait. Setara Win32 akan menjadi objek acara melalui CreateEvent yang sebenarnya juga ada di .NET sebagai WaitHandles. Model Pulse / Wait mirip dengan pthread_signal dan pthread_wait Unix tetapi lebih cepat karena mereka bisa sepenuhnya operasi mode pengguna dalam kasus un-contended.

Monitor.Pulse / Tunggu mudah digunakan. Dalam satu utas, kita mengunci objek, memeriksa bendera / negara bagian / properti dan jika bukan itu yang kita harapkan, hubungi Monitor. Tunggu yang akan melepaskan kunci dan tunggu sampai pulsa dikirim. Ketika menunggu kembali, kami kembali dan memeriksa kembali flag / state / property. Di utas lainnya, kita mengunci objek setiap kali kita mengubah flag / state / property dan kemudian memanggil PulseAll untuk membangunkan utas dengar.

Seringkali kita ingin agar kelas kita aman dari benang sehingga kita memasukkan kunci ke dalam kode kita. Namun, sering terjadi bahwa kelas kita hanya akan digunakan oleh satu utas. Ini berarti kunci tidak perlu memperlambat kode kami ... ini adalah di mana optimisasi cerdas dalam CLR dapat membantu meningkatkan kinerja.

Saya tidak yakin tentang penerapan kunci Microsoft tetapi di DotGNU dan Mono, bendera negara kunci disimpan di header setiap objek. Setiap objek di .NET (dan Java) dapat menjadi kunci sehingga setiap objek perlu mendukung ini di header mereka. Dalam implementasi DotGNU, ada bendera yang memungkinkan Anda untuk menggunakan hashtable global untuk setiap objek yang digunakan sebagai kunci - ini memiliki manfaat menghilangkan overhead 4 byte untuk setiap objek. Ini tidak bagus untuk memori (terutama untuk sistem tertanam yang tidak sangat berulir) tetapi memiliki kinerja yang baik.

Baik Mono dan DotGNU secara efektif menggunakan mutex untuk melakukan penguncian / menunggu tetapi menggunakan operasi perbandingan-dan-pertukaran gaya spinlock untuk menghilangkan kebutuhan untuk benar-benar melakukan kunci keras kecuali benar-benar diperlukan:

Anda dapat melihat contoh bagaimana monitor dapat diterapkan di sini:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup


9

Peringatan tambahan untuk mengunci Mutex bersama yang Anda identifikasi dengan string ID adalah bahwa itu akan default ke mutex "Lokal \" dan tidak akan dibagikan di seluruh sesi di lingkungan server terminal.

Awali pengenal string Anda dengan "Global \" untuk memastikan bahwa akses ke sumber daya sistem bersama dikontrol dengan benar. Saya baru saja mengalami sejumlah masalah sinkronisasi komunikasi dengan layanan yang berjalan di bawah akun SYSTEM sebelum saya menyadari hal ini.


5

Saya akan mencoba untuk menghindari "kunci ()", "Mutex" dan "Monitor" jika Anda dapat ...

Lihat namespace System.Collections.Concurrent di .NET 4 yang baru
ini memiliki beberapa kelas koleksi thread-safe yang bagus

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

Batu ConcurrentDictionary! tidak ada penguncian manual lagi untuk saya!


2
Hindari mengunci tetapi gunakan Monitor? Mengapa?
mafu

@ mafutrct Karena Anda harus menjaga sendiri sinkronisasi.
Peter Gfader

Oh, sekarang saya mengerti, Anda bermaksud menghindari SEMUA dari tiga ide yang disebutkan. Kedengarannya seperti Anda akan menggunakan Monitor tetapi tidak menggunakan kunci / Mutex.
mafu

Jangan pernah menggunakan System.Collections.Concurrent. Mereka adalah sumber utama kondisi lomba, dan juga memblokir utas penelepon.
Alexander Danilov

-2

Dalam kebanyakan kasus, Anda tidak boleh menggunakan kunci (= Monitor) atau mutex / semaphores. Mereka semua memblokir utas saat ini.

Dan kamu pasti tidak boleh menggunakan System.Collections.Concurrent kelas - mereka adalah sumber utama kondisi lomba karena tidak mendukung transaksi antara banyak koleksi, dan juga memblokir utas saat ini.

Anehnya .NET tidak memiliki mekanisme sinkronisasi yang efektif.

Saya menerapkan antrian serial dari GCD ( Objc/Swiftdunia) di C # - sangat ringan, tidak menghalangi alat sinkronisasi yang menggunakan kumpulan benang, dengan pengujian.

Ini adalah cara terbaik untuk menyinkronkan apa pun dalam banyak kasus - dari akses basis data (halo sqlite) ke logika bisnis.

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.