Ini adalah bentuk yang buruk untuk digunakan this
dalam pernyataan kunci karena umumnya di luar kendali Anda siapa lagi yang mungkin mengunci objek itu.
Untuk merencanakan operasi paralel dengan semestinya, perhatian khusus harus diberikan untuk mempertimbangkan kemungkinan situasi jalan buntu, dan memiliki jumlah titik masuk kunci yang tidak diketahui menghalangi hal ini. Misalnya, siapa pun yang memiliki referensi ke objek dapat menguncinya tanpa perancang / pembuat objek mengetahuinya. Ini meningkatkan kompleksitas solusi multi-utas dan mungkin memengaruhi kebenarannya.
Bidang pribadi biasanya merupakan opsi yang lebih baik karena kompiler akan memberlakukan pembatasan akses padanya, dan itu akan merangkum mekanisme penguncian. Menggunakan this
melanggar enkapsulasi dengan memaparkan sebagian dari implementasi penguncian Anda kepada publik. Juga tidak jelas bahwa Anda akan mendapatkan kunci this
kecuali jika telah didokumentasikan. Bahkan kemudian, mengandalkan dokumentasi untuk mencegah masalah tidak optimal.
Akhirnya, ada kesalahpahaman umum yang lock(this)
benar - benar memodifikasi objek yang dilewatkan sebagai parameter, dan dalam beberapa cara membuatnya hanya-baca atau tidak dapat diakses. Ini salah . Objek diteruskan sebagai parameter lock
hanya berfungsi sebagai kunci . Jika kunci sudah ditahan pada kunci itu, kunci tidak dapat dibuat; jika tidak, kunci diizinkan.
Inilah sebabnya mengapa buruk untuk menggunakan string sebagai kunci dalam lock
pernyataan, karena mereka tidak dapat diubah dan dibagikan / diakses di seluruh bagian aplikasi. Anda harus menggunakan variabel pribadi sebagai gantinya, sebuah Object
instance akan melakukannya dengan baik.
Jalankan kode C # berikut sebagai contoh.
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public void LockThis()
{
lock (this)
{
System.Threading.Thread.Sleep(10000);
}
}
}
class Program
{
static void Main(string[] args)
{
var nancy = new Person {Name = "Nancy Drew", Age = 15};
var a = new Thread(nancy.LockThis);
a.Start();
var b = new Thread(Timewarp);
b.Start(nancy);
Thread.Sleep(10);
var anotherNancy = new Person { Name = "Nancy Drew", Age = 50 };
var c = new Thread(NameChange);
c.Start(anotherNancy);
a.Join();
Console.ReadLine();
}
static void Timewarp(object subject)
{
var person = subject as Person;
if (person == null) throw new ArgumentNullException("subject");
// A lock does not make the object read-only.
lock (person.Name)
{
while (person.Age <= 23)
{
// There will be a lock on 'person' due to the LockThis method running in another thread
if (Monitor.TryEnter(person, 10) == false)
{
Console.WriteLine("'this' person is locked!");
}
else Monitor.Exit(person);
person.Age++;
if(person.Age == 18)
{
// Changing the 'person.Name' value doesn't change the lock...
person.Name = "Nancy Smith";
}
Console.WriteLine("{0} is {1} years old.", person.Name, person.Age);
}
}
}
static void NameChange(object subject)
{
var person = subject as Person;
if (person == null) throw new ArgumentNullException("subject");
// You should avoid locking on strings, since they are immutable.
if (Monitor.TryEnter(person.Name, 30) == false)
{
Console.WriteLine("Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string \"Nancy Drew\".");
}
else Monitor.Exit(person.Name);
if (Monitor.TryEnter("Nancy Drew", 30) == false)
{
Console.WriteLine("Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!");
}
else Monitor.Exit("Nancy Drew");
if (Monitor.TryEnter(person.Name, 10000))
{
string oldName = person.Name;
person.Name = "Nancy Callahan";
Console.WriteLine("Name changed from '{0}' to '{1}'.", oldName, person.Name);
}
else Monitor.Exit(person.Name);
}
}
Output konsol
'this' person is locked!
Nancy Drew is 16 years old.
'this' person is locked!
Nancy Drew is 17 years old.
Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string "Nancy Drew".
'this' person is locked!
Nancy Smith is 18 years old.
'this' person is locked!
Nancy Smith is 19 years old.
'this' person is locked!
Nancy Smith is 20 years old.
Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!
'this' person is locked!
Nancy Smith is 21 years old.
'this' person is locked!
Nancy Smith is 22 years old.
'this' person is locked!
Nancy Smith is 23 years old.
'this' person is locked!
Nancy Smith is 24 years old.
Name changed from 'Nancy Drew' to 'Nancy Callahan'.