Це погана форма для використання this
в операторах блокування, оскільки це, як правило, поза вашим контролем, хто ще може заблокувати цей об'єкт.
Для правильного планування паралельних операцій слід особливо обережно враховувати можливі ситуації з тупиком, а наявність невідомої кількості точок входу блокування перешкоджає цьому. Наприклад, будь-який із посиланням на об'єкт може заблокувати його, не знаючи про це дизайнера / творця об'єкта. Це збільшує складність багатопотокових рішень і може вплинути на їх правильність.
Приватне поле зазвичай є кращим варіантом, оскільки компілятор буде застосовувати обмеження доступу до нього, і він інкапсулює механізм блокування. Використання this
порушує інкапсуляцію, викриваючи частину вашої реалізації блокування. Також незрозуміло, що ви придбаєте замок, this
якщо він не буде задокументований. Вже тоді, покладаючись на документацію для запобігання проблеми, є неоптимальним.
Нарешті, існує поширена помилкова думка, яка lock(this)
фактично модифікує об'єкт, переданий як параметр, і певним чином робить його лише для читання або недоступним. Це помилково . Об'єкт, переданий як параметр, lock
просто служить ключем . Якщо блокування вже тримається на цій клавіші, блокування неможливо зробити; в іншому випадку блокування дозволено.
Ось чому погано використовувати рядки як ключі у lock
висловлюваннях, оскільки вони незмінні та поділяються / доступні в усіх частинах програми. Ви повинні використовувати приватну змінну замість цього, Object
екземпляр буде добре робити.
Запустіть наступний код C # як приклад.
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);
}
}
Вихід консолі
'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'.