C'est une mauvaise forme à utiliser this
dans les instructions de verrouillage, car il est généralement hors de votre contrôle qui d'autre pourrait verrouiller cet objet.
Afin de planifier correctement les opérations parallèles, une attention particulière doit être portée à la prise en compte d'éventuelles situations de blocage, et le fait d'avoir un nombre inconnu de points d'entrée de verrouillage empêche cela. Par exemple, toute personne ayant une référence à l'objet peut le verrouiller sans que le concepteur / créateur d'objet le sache. Cela augmente la complexité des solutions multithread et peut affecter leur exactitude.
Un champ privé est généralement une meilleure option car le compilateur lui imposera des restrictions d'accès et encapsulera le mécanisme de verrouillage. L'utilisation this
viole l'encapsulation en exposant une partie de votre implémentation de verrouillage au public. Il n'est pas clair non plus que vous obtiendrez un verrou à this
moins qu'il n'ait été documenté. Même alors, se fier à la documentation pour éviter un problème n'est pas optimal.
Enfin, il y a l'idée fausse courante qui lock(this)
modifie réellement l'objet passé en paramètre, et le rend en quelque sorte en lecture seule ou inaccessible. C'est faux . L'objet passé en paramètre lock
sert simplement de clé . Si un verrou est déjà maintenu sur cette clé, le verrouillage ne peut pas être effectué; sinon, le verrouillage est autorisé.
C'est pourquoi il est mauvais d'utiliser des chaînes comme clés dans les lock
instructions, car elles sont immuables et sont partagées / accessibles dans toutes les parties de l'application. Vous devriez utiliser une variable privée à la place, une Object
instance fera l'affaire.
Exécutez le code C # suivant comme exemple.
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);
}
}
Sortie console
'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'.