Это плохой форме использовать this
в операторах блокировки, потому что, как правило, вне вашего контроля, кто-то может быть запирающего на этом объекте.
Для правильной планировки параллельных операций необходимо соблюдать особую осторожность, чтобы рассмотреть возможные ситуации взаимоблокировки, и наличие неизвестного количества точек входа блокировки препятствует этому. Например, любой, кто ссылается на объект, может заблокировать его, не зная об этом конструктор/создатель объекта. Это увеличивает сложность многопоточных решений и может повлиять на их правильность.
Частное поле, как правило, является лучшим вариантом, поскольку компилятор будет применять к нему ограничения доступа, и он будет инкапсулировать механизм блокировки. Использование this
нарушает инкапсуляцию, подвергая часть реализации блокировки публике. Также не ясно, что вы приобретете замок на this
, если он не был задокументирован. Даже тогда, полагаясь на документацию, чтобы предотвратить проблему, является субоптимальным.
Наконец, существует общее заблуждение, что lock(this)
фактически изменяет объект, переданный как параметр, и каким-то образом делает его доступным только для чтения или недоступным. Это false. Объект, переданный как параметр 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'.
это код лота! – 2008-10-30 21:17:40
Imo слишком много кода, чтобы действительно проиллюстрировать проблему. – Tigraine 2008-10-30 21:24:38