Nó là hình thức xấu để sử dụng this
trong các câu lệnh khóa bởi vì nó thường nằm ngoài tầm kiểm soát của bạn, những người khác có thể đang khóa đối tượng đó.
Để lập kế hoạch đúng cho các hoạt động song song, cần đặc biệt chú ý đến các tình huống bế tắc có thể xảy ra và việc có một số điểm nhập cảnh khóa không xác định cản trở điều này. Ví dụ, bất kỳ ai có tham chiếu đến đối tượng đều có thể khóa nó mà không cần người thiết kế / người tạo đối tượng biết về nó. Điều này làm tăng sự phức tạp của các giải pháp đa luồng và có thể ảnh hưởng đến tính chính xác của chúng.
Một trường riêng thường là một tùy chọn tốt hơn vì trình biên dịch sẽ thực thi các hạn chế truy cập đối với nó và nó sẽ đóng gói cơ chế khóa. Sử dụng this
vi phạm đóng gói bằng cách phơi bày một phần việc triển khai khóa của bạn ra công chúng. Nó cũng không rõ ràng rằng bạn sẽ có được một khóa trên this
trừ khi nó đã được ghi nhận. Ngay cả sau đó, dựa vào tài liệu để ngăn chặn một vấn đề là tối ưu.
Cuối cùng, có một quan niệm sai lầm phổ biến lock(this)
thực sự sửa đổi đối tượng được truyền dưới dạng tham số và theo một cách nào đó làm cho nó chỉ đọc hoặc không thể truy cập được. Điều này là sai . Đối tượng được truyền dưới dạng tham số để lock
chỉ đóng vai trò là khóa . Nếu một khóa đã được giữ trên phím đó, khóa không thể được thực hiện; nếu không, khóa được cho phép.
Đây là lý do tại sao thật tệ khi sử dụng các chuỗi làm khóa trong các lock
câu lệnh, vì chúng không thay đổi và được chia sẻ / truy cập trên các phần của ứng dụng. Bạn nên sử dụng một biến riêng tư thay vào đó, một Object
thể hiện sẽ làm tốt.
Chạy mã C # sau đây làm ví dụ.
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);
}
}
Bảng điều khiển đầu ra
'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'.