Es una mala forma de usar this
en las declaraciones de bloqueo porque generalmente está fuera de su control quién más podría estar bloqueando ese objeto.
Para planificar adecuadamente las operaciones paralelas, se debe tener especial cuidado para considerar posibles situaciones de punto muerto, y tener un número desconocido de puntos de entrada de bloqueo dificulta esto. Por ejemplo, cualquiera con una referencia al objeto puede bloquearlo sin que el diseñador / creador del objeto lo sepa. Esto aumenta la complejidad de las soluciones de subprocesos múltiples y puede afectar su corrección.
Un campo privado suele ser una mejor opción, ya que el compilador impondrá restricciones de acceso y encapsulará el mecanismo de bloqueo. El uso this
viola la encapsulación al exponer parte de su implementación de bloqueo al público. Tampoco está claro que va a adquirir un bloqueo a this
menos que se haya documentado. Incluso entonces, confiar en la documentación para evitar un problema es subóptimo.
Finalmente, existe la idea errónea común que en lock(this)
realidad modifica el objeto pasado como parámetro, y de alguna manera lo hace de solo lectura o inaccesible. Esto es falso . El objeto pasado como parámetro lock
simplemente sirve como clave . Si ya se está reteniendo un candado en esa llave, no se puede hacer el candado; de lo contrario, se permite el bloqueo.
Esta es la razón por la cual es malo usar cadenas como claves en las lock
declaraciones, ya que son inmutables y son compartidas / accesibles a través de partes de la aplicación. En su lugar, debe usar una variable privada, una Object
instancia funcionará bien.
Ejecute el siguiente código C # como ejemplo.
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);
}
}
Salida de la consola
'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'.
lock(this)
es un consejo estándar; Es importante tener en cuenta que hacerlo generalmente hará que sea imposible que el código externo provoque que el bloqueo asociado con el objeto se retenga entre llamadas de método. Esto puede o no ser algo bueno . Existe cierto peligro al permitir que el código externo mantenga un bloqueo durante una duración arbitraria, y las clases generalmente deben diseñarse para hacer innecesario dicho uso, pero no siempre hay alternativas prácticas. Como un simple ejemplo, a menos que un aperos de recogida de unaToArray
oToList
método de su propia ...there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false
- Creo que esas conversaciones son sobre el bit SyncBlock en el objeto CLR, así que formalmente es correcto - bloquear el objeto modificado en sí mismo