É péssimo usar this
nas instruções de bloqueio, porque geralmente está fora de seu controle quem mais pode estar bloqueando esse objeto.
Para planejar adequadamente as operações paralelas, deve-se tomar cuidado especial para considerar possíveis situações de conflito, e ter um número desconhecido de pontos de entrada de bloqueio dificulta isso. Por exemplo, qualquer pessoa com uma referência ao objeto pode travá-lo sem que o criador / criador do objeto saiba disso. Isso aumenta a complexidade das soluções multithread e pode afetar sua correção.
Um campo privado geralmente é uma opção melhor, pois o compilador aplicará restrições de acesso a ele e encapsulará o mecanismo de bloqueio. O uso this
viola o encapsulamento, expondo parte da sua implementação de bloqueio ao público. Também não está claro que você estará adquirindo um bloqueio, a this
menos que tenha sido documentado. Mesmo assim, depender da documentação para evitar um problema é sub-ideal.
Finalmente, existe o equívoco comum que lock(this)
realmente modifica o objeto passado como parâmetro e, de alguma forma, o torna somente leitura ou inacessível. Isso é falso . O objeto passado como parâmetro lock
apenas serve como chave . Se uma fechadura já estiver sendo mantida nessa chave, a fechadura não poderá ser feita; caso contrário, o bloqueio é permitido.
É por isso que é ruim usar cadeias de caracteres como as chaves nas lock
instruções, pois elas são imutáveis e são compartilhadas / acessíveis em partes do aplicativo. Você deve usar uma variável privada em vez disso, uma Object
instância será bem.
Execute o seguinte código C # como um exemplo.
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);
}
}
Saída do 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'.