Versi C # dari kata kunci yang disinkronkan java?

313

Apakah c # memiliki versi sendiri dari kata kunci "disinkronkan" java?

Yaitu di java itu dapat ditentukan baik untuk fungsi, objek atau blok kode, seperti:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}

atau

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
sumber
3
Formulir blok memerlukan referensi untuk dikunci. Dalam bentuk metode objek kunci secara implisit ini (atau Class [this.class, bukan getClass ()] untuk metode statis, tetapi jangan mengunci Classes).
Tom Hawtin - tackline
1
Masih belum dilindungi? Saya selalu datang ke sini karena saya tidak dapat mengingat [MethodImpl(MethodImplOptions.Synchronized)]kalimat itu.
Bitterblue
1
Saya pikir cuplikan kedua Anda tidak dapat dikompilasi - perlu disinkronkan pada sesuatu.
PoweredByRice

Jawaban:

466

Pertama - sebagian besar kelas tidak perlu thread-safe. Gunakan YAGNI : hanya terapkan keamanan utas saat Anda tahu Anda benar-benar akan menggunakannya (dan mengujinya).

Untuk hal-hal tingkat metode, ada [MethodImpl]:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {/* code */}

Ini juga dapat digunakan pada pengakses (properti dan acara):

private int i;
public int SomeProperty
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return i; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { i = value; }
}

Perhatikan bahwa bidang-seperti peristiwa yang disinkronkan secara default, sedangkan sifat auto-dilaksanakan tidak :

public int SomeProperty {get;set;} // not synchronized
public event EventHandler SomeEvent; // synchronized

Secara pribadi, saya tidak suka implementasi MethodImplkarena mengunci thisatau typeof(Foo)- yang bertentangan dengan praktik terbaik. Opsi yang disukai adalah menggunakan kunci Anda sendiri:

private readonly object syncLock = new object();
public void SomeMethod() {
    lock(syncLock) { /* code */ }
}

Perhatikan bahwa untuk kejadian seperti lapangan, implementasi penguncian bergantung pada kompiler; dalam kompiler Microsoft yang lebih lama itu adalah lock(this)/ lock(Type)- namun, dalam kompiler yang lebih baru ia menggunakanInterlocked pembaruan - jadi aman-aman tanpa bagian-bagian yang buruk.

Ini memungkinkan penggunaan yang lebih terperinci, dan memungkinkan penggunaan Monitor.Wait/ Monitor.Pulseetc untuk berkomunikasi di antara utas.

Entri blog terkait (nanti ditinjau kembali ).

Marc Gravell
sumber
@earcam dan pertanyaan Anda adalah? Pernyataan itu benar. Sebagian besar kelas tidak memiliki persyaratan untuk benang-aman, tidak akan diuji untuk keselamatan benang, dan memiliki keselamatan benang akan berdampak pada kinerja. Jumlah jenis yang benar-benar perlu dikhawatirkan tentang utas sangat kecil - koleksi yang sengaja disinkronkan, multiplexer, dll.
Marc Gravell
4
Saya pikir saya seharusnya hanya menyatakan; "sebagian besar kelas tidak perlu aman" tetapi "semua pengembang harus sadar mata uang". Dalam retrospeksi, saya setuju bahwa jumlahnya sangat kecil (dan pasti sesuatu yang ingin Anda dapatkan dengan benar sekali di satu tempat, memungkinkan sebagian besar kelas berinteraksi tanpa memperhatikan lingkungan multi-utas mereka). Seandainya saya menghapus komentar lebih cepat =)
earcam
6
Posting blog tertaut Marc memiliki tindak lanjut Maret 2010 yang mengatakan bahwa dalam. NET 4.0, MethodImpldan peristiwa seperti lapangan sekarang menghasilkan kode sinkronisasi yang baik, dan tidak perlu lagi menggunakan kunci Anda sendiri.
Rory O'Kane
2
Sebagian besar aplikasi saat ini berbasis web, disajikan dengan kerangka kerja yang bergantung pada penggunaan ulang yang berat dan siklus hidup objek yang kompleks melalui ketergantungan-injeksi. Pola pikir default akhir-akhir ini cenderung salah pada sisi keamanan benang.
Sheepy
1
@ Elazar terlambat sekarang, tetapi sebagai catatan: mengubah kerangka adalah perubahan satu-baris untuk mereka csproj jika Anda telah membuatnya menggunakan templat inti standar / bersih .net - dan .net biasa tersedia, seperti multi -menunggu. Namun, IDE tooling di sekitar ini benar-benar mengerikan - Anda hanya perlu tahu apa yang dapat Anda ubah dan untuk apa :)
Marc Gravell
56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Jan Gressmann
sumber
10
Anda yakin ingin mendeklarasikan objek kunci sebagai statis ..?
serg10
21
Tentu, jadi setiap Thread dapat dengan mudah mengaksesnya tanpa melewati referensi.
Jan Gressmann
33
Jika kita berada dalam konteks pertanyaan si penanya, maka kita berbicara tentang metode contoh. Menggunakan statis berarti bahwa jika utas 1 memanggil instance1.DoSomething () dan utas 2 memanggil instance2.DoSomething, panggilan kedua akan memblokir meskipun itu adalah objek yang sama sekali berbeda. panggilan thread2 tidak boleh diblokir kecuali seseorang memanggil DoSomething pada objek yang sama . Bukan mengatakan Anda salah, tetapi mengatakan penting untuk memahami efek menggunakan statis di sini, karena hal itu dapat menyebabkan kinerja yang buruk dengan memblokir secara global alih-alih berdasarkan contoh per contoh.
AaronLS
1
@ HarunLS Kunci statis jika sangat berguna ketika objek Anda melakukan tindakan pada ruang lingkup yang lebih besar daripada dirinya sendiri. Selalu terjadi dengan layanan web misalnya.
Thibault D.
4
-1 karena ini adalah perilaku yang berbeda dari yang diminta OP. Ini adalah kunci kelas, bukan kunci instance.
kisah
39

Apakah c # memiliki versi sendiri dari kata kunci "disinkronkan" java?

Tidak. Dalam C #, Anda secara eksplisit locksumber daya yang ingin Anda kerjakan secara sinkron di utas asinkron. lockmembuka blok; itu tidak berfungsi pada level metode.

Namun, mekanisme yang mendasarinya serupa karena lockbekerja dengan memohon Monitor.Enter(dan selanjutnya Monitor.Exit) pada runtime. Java bekerja dengan cara yang sama, menurut dokumentasi Sun .

Konrad Rudolph
sumber
3
Itu tidak memiliki "kata kunci" yang setara, tetapi seperti yang ditunjukkan oleh jawaban Marc Gravell di atas, Anda dapat menyinkronkan pada level metode menggunakan anotasi [MethodImpl (MethodImplOptions.Synchronized)].
MindJuice
1
Karena synchronizedmetode Java pada dasarnya synchronized (this.getClass())tidak akan sama pada C # menjadi lock(typeof(this))?
Sri Harsha Chilakapati
2
@SriHarshaChilakapati itu hanya sebagian benar, synchronizedkata kunci java pada metode lebih seperti :, synchronized(this)hanya pada metode statis berperilaku seperti itu synchronized(class).
bvdb
6

Perhatikan, dengan jalur penuh garis: [MethodImpl(MethodImplOptions.Synchronized)]akan terlihat seperti

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

Traubenfuchs
sumber
1
atau Anda bisa menggunakanusing System.Runtime.CompilerServices;
aloisdg pindah ke codidact.com
Saya menulis komentar itu ketika saya belum tahu tentang memasukkan secara otomatis menggunakan pernyataan, setelah memprogram C # untuk tidak lebih dari beberapa hari atau minggu dan saya kagum dengan 3 upvotes itu.
Traubenfuchs
1
Anda membantu setidaknya 3 devs dan itu bagus :)
aloisdg pindah ke codidact.com
5

Anda bisa menggunakan lockpernyataan itu sebagai gantinya. Saya pikir ini hanya bisa menggantikan versi kedua. Juga, ingat bahwa keduanya synchronizeddan lockperlu beroperasi pada suatu objek.

James
sumber