Jest to zła forma do użycia this
w instrukcjach blokady, ponieważ generalnie nie masz kontroli, kto jeszcze mógłby zablokować ten obiekt.
Aby właściwie zaplanować operacje równoległe, należy zwrócić szczególną uwagę na możliwe sytuacje zakleszczenia, a utrudnia to nieznana liczba punktów wejścia do śluzy. Na przykład każdy, kto ma odniesienie do obiektu, może się na nim zablokować bez wiedzy projektanta / twórcy obiektu. Zwiększa to złożoność rozwiązań wielowątkowych i może wpływać na ich poprawność.
Pole prywatne jest zwykle lepszą opcją, ponieważ kompilator będzie wymuszał ograniczenia dostępu do niego i obuduje mechanizm blokujący. Używanie this
narusza enkapsulację, udostępniając publicznie część implementacji blokowania. Nie jest również jasne, że uzyskasz blokadę, this
chyba że została udokumentowana. Nawet wtedy poleganie na dokumentacji, aby zapobiec problemowi, nie jest optymalne.
Wreszcie istnieje powszechne nieporozumienie, które lock(this)
faktycznie modyfikuje obiekt przekazywany jako parametr i w pewien sposób czyni go tylko do odczytu lub niedostępnym. To jest fałsz . Obiekt przekazany jako parametr lock
służy jedynie jako klucz . Jeśli blokada jest już utrzymywana na tym kluczu, blokada nie może być wykonana; w przeciwnym razie blokada jest dozwolona.
Dlatego źle jest używać ciągów jako kluczy w lock
instrukcjach, ponieważ są one niezmienne i są udostępniane / dostępne w różnych częściach aplikacji. Zamiast tego powinieneś użyć zmiennej prywatnej, Object
instancja dobrze się sprawdzi.
Uruchom następujący kod C # jako przykład.
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);
}
}
Wyjście konsoli
'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'.