Die Verwendung this
in Sperranweisungen ist eine schlechte Form, da Sie im Allgemeinen nicht die Kontrolle darüber haben, wer dieses Objekt sonst möglicherweise sperrt.
Um Paralleloperationen richtig planen zu können, sollte besonders darauf geachtet werden, mögliche Deadlock-Situationen zu berücksichtigen, und eine unbekannte Anzahl von Lock-Einstiegspunkten behindert dies. Zum Beispiel kann jeder, der auf das Objekt verweist, es sperren, ohne dass der Objektdesigner / -ersteller davon erfährt. Dies erhöht die Komplexität von Multithread-Lösungen und kann deren Richtigkeit beeinträchtigen.
Ein privates Feld ist normalerweise eine bessere Option, da der Compiler Zugriffsbeschränkungen erzwingt und den Sperrmechanismus kapselt. Die Verwendung this
verletzt die Kapselung, indem ein Teil Ihrer Sperrimplementierung der Öffentlichkeit zugänglich gemacht wird. Es ist auch nicht klar, dass Sie eine Sperre erwerben, this
es sei denn, dies wurde dokumentiert. Selbst dann ist es nicht optimal, sich auf die Dokumentation zu verlassen, um ein Problem zu vermeiden.
Schließlich gibt es das verbreitete Missverständnis, dass lock(this)
das als Parameter übergebene Objekt tatsächlich geändert und in gewisser Weise schreibgeschützt oder unzugänglich gemacht wird. Das ist falsch . Das als Parameter übergebene Objekt lock
dient lediglich als Schlüssel . Wenn dieser Schlüssel bereits gesperrt ist, kann das Schloss nicht hergestellt werden. Andernfalls ist die Sperre zulässig.
Aus diesem Grund ist es schlecht, Zeichenfolgen als Schlüssel in lock
Anweisungen zu verwenden, da sie unveränderlich sind und von Teilen der Anwendung gemeinsam genutzt werden können. Sie sollten stattdessen eine private Variable verwenden, eine Object
Instanz funktioniert gut.
Führen Sie den folgenden C # -Code als Beispiel aus.
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);
}
}
Konsolenausgabe
'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'.