C#多线程共享锁

3
有没有一种常见的方法可以在操作同一组数据的不同对象之间“共享”锁?
我知道通常不建议使用公共对象来进行锁定。
例如,队列可以实现为线程安全的,但是某些其他类可能需要特定的锁来锁定多个队列操作。如果我有第三个类,它还需要对此相同的队列实例执行多个锁定操作会发生什么?
例如: (假设 L<T> 是线程安全列表,只是为了节省一些打字)
class Worker
{
    private readonly L<Something> _list;
    public Worker(L<Something> list) { _list = list; }

    private readonly object _lock = new object();
    public void Replace(Something old, Something new)
    {
       lock (_lock) 
       {
          if (_list.Contains(old))
          {
              _list.Remove(old);
              _list.Add(new);
          }
       }
    }
}

如果在不同的线程上,有其他类在if条件之后删除了old元素,则由于_lock是私有对象,列表将不再包含该元素。

我应该锁定实际的列表实例吗?

3个回答

10

不要将此列表暴露为属性,仅公开与其交互的方法。这样,您可以在一个类中处理所有锁定操作,而不必处理公共锁定对象。


我理解你的观点,但在这种情况下,与该数据交互的所有方法必须属于同一个类。你不能有一个执行操作序列并且同时又是线程安全的不同类。 - vgru
@Groo 关于Robert Grant的评论 - 没关系,他是个本地傻瓜。 - cjk
我相信你必须拥有2000声望才能做到这一点。 - Rob Grant

9

一种常见的方法是暴露一个属性,例如 ICollection.SyncRoot。当然,每个人都必须遵守锁定才能使其正常工作。

如果可能的话,请避免这种方法,并像ck建议的那样封装操作,这样更加健壮和易于理解。


谢谢,这是我问题的正确答案:共享锁的常见方式。我知道其中的影响,但只是想看看是否有第三种可能性我不知道的。 - vgru

5

从技术上讲,您可以很容易地暴露锁对象。但问题在于不建议公开它们的真正原因是死锁。

如果锁对象被公开,就会引入风险,即其他某些代码将锁定该对象,而不考虑锁定顺序。这反过来可能导致死锁。

即使锁未明确公开,同样存在这种危险。

最好的方法是根本不要公开锁 - 无论是隐式还是显式。换句话说,完全将它们嵌入提供服务的类中。不幸的是,有时这并不是一个选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接