3

考虑以下代码:

static void AddItem()
{
    lock (_list) 
        _list.Add ("Item " + _list.Count); //Lock 1

    string[] items;
    lock (_list) 
        items = _list.ToArray(); //Lock 2
    foreach (string s in items) 
        Console.WriteLine (s);
}

如果线程 A 获得了锁 2,而线程 B 试图获得锁 1,那么 B 是否会获得锁?考虑到两个锁使用相同的锁定对象。

4

5 回答 5

5

不,线程 B 需要等到线程 A 释放锁。毕竟,这就是它是同一个 lock 对象的意义——只有一个 lock。获取或释放锁的位置无关紧要:一次只有一个线程可以“拥有”监视器。

顺便说一句,我强烈建议您使用大括号来提高可读性:

lock(_list)
{
    _list.Add(...);
}
于 2012-08-14T12:50:48.730 回答
2

不,因为它们使用相同的锁定对象,所以它们是互斥的。

通常代码用于锁定对象(例如列表)以对其执行操作而不受其他线程的干扰。这要求无论执行什么操作,项目都被锁定。

详细地说,假设您有一个设计为线程安全的列表。如果您尝试同时添加和删除多个项目,您可能会损坏列表。通过在需要修改时锁定列表,您可以帮助确保线程安全。

这一切都取决于一个对象将使所有锁互斥这一事实。

于 2012-08-14T12:51:07.373 回答
2

不,B不会。两者都锁定在同一个对象上,因此这两个锁是“链接的”。因此,如果您需要高度优化此类代码,有时您可能会考虑使用多个锁对象。

附带说明一下,您不应该锁定列表本身,而是锁定object专门为此目的创建的列表。

于 2012-08-14T12:51:53.503 回答
1

考虑一下:

lock(obj)
{
 //Do Stuff;
}

是以下的简写:

Monitor.Enter(obj);
try
{
  //Do Stuff;
}
finally
{
  Monitor.Exit(obj);
}

现在考虑这Monitor.Enter()是一个与其他方法一样的方法调用。它对代码中调用它的位置一无所知。它唯一知道的是传递给它的对象。

就它而言,你所说的“锁1”和“锁2”是同一个锁。

于 2012-08-14T13:10:05.440 回答
1

如果线程 A 正在使用锁,则没有其他线程可以使用它(无论在哪里使用锁)。因此,线程 B 将被阻塞,直到该锁空闲。

于 2012-08-14T12:52:03.410 回答