为什么将lock(this)视为不良实践?

30 浏览
0 Comments

为什么将lock(this)视为不良实践?

MSDN文档中说:

public class SomeObject
{
  public void SomeOperation()
  {
    lock(this)
    {
      //Access instance variables
    }
  }
}

“如果实例可以公开访问,则会有问题”。我想知道为什么?是因为锁定时间超过必要时间?还是有其他更加隐蔽的原因?

admin 更改状态以发布 2023年5月24日
0
0 Comments

因为如果人们能够获取到您的对象实例(即您的this指针),那么他们也可以尝试锁定同一个对象。现在他们可能没有意识到您在内部锁定了this,所以这可能会导致问题(可能是死锁)。\n此外,这也是一种不好的做法,因为它锁定了\"太多\"内容。\n例如,您可能有一个List成员变量,而您实际上需要锁定的只是该成员变量。如果您在函数中锁定整个对象,则调用这些函数的其他内容将被阻塞等待锁定。如果这些函数不需要访问成员列表,您将会导致其他代码等待,并且没有任何原因减慢您的应用程序速度。

0
0 Comments

在锁定语句中使用this是不好的形式,因为通常谁会锁定那个对象是不受你控制的。

为了正确地规划并行操作,应该特别注意可能发生死锁的情况,并且锁定入口点的数量未知会妨碍这一点。例如,任何一个对该对象有引用的人都可以锁定它,而对象的设计者/创建者可能并不知道。这增加了多线程解决方案的复杂性,可能影响它们的正确性。

通常情况下,私有字段是更好的选择,因为编译器将强制执行对它的访问限制,同时还将封装锁定机制。使用this会通过将锁定实现的部分公开来破坏封装。除非它已经被记录,否则无法明确地确定你是否会获取this的锁。即使如此,依赖文档来防止问题也不是最佳选择。

最后,有一个常见的误解认为lock(this)实际上修改了作为参数传递的对象,并以某种方式使其为只读或不可访问。这是错误的。作为参数传递给lock的对象仅作为。如果已经在该键上保持了锁定,则无法进行锁定;否则允许锁定。

这就是为什么在lock语句中使用字符串作为键很糟糕,因为它们是不可变的,并且在应用程序的各个部分共享/可访问。您应该使用一个私有变量,一个Object实例会很好。

以以下C#代码为例。

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);
    }
}

控制台输出

'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'.

0