2种不同场景下的锁性能

发布于 2024-10-09 02:02:36 字数 785 浏览 0 评论 0原文

我有一个包含 2 个内联列表的类,其中包含超过 1000 个元素,

public class A
{
    private List<long> aList = new List<long>();
    private List<long> bList = new List<long>();
    void MethodThatUsesAList()
    {
      //Works with aList
    }
    void MethodThatUsesBList()
    {
      //Works with bList
    }
    void MethodThatUsesBoth()
    {
      //Works with both lists
    }
}

现在,我想让这个类线程安全,并且我看到使用锁有 2 种不同的场景,首先

object _synchObject = new object()

在所有方法中创建并锁定这个对象, 或者第二个在使用每个列表时锁定它, 我的第一个问题是,哪种方法是首选? 如果首选第二种,那么使用这样的锁是否会产生性能问题?

    void MethodThatUsesBoth()
    {
       lock(aList){
           lock(bList){
                 //Works with both lists
           }
       }
    }

I have a class with 2 inline lists containing more than 1000 element like this,

public class A
{
    private List<long> aList = new List<long>();
    private List<long> bList = new List<long>();
    void MethodThatUsesAList()
    {
      //Works with aList
    }
    void MethodThatUsesBList()
    {
      //Works with bList
    }
    void MethodThatUsesBoth()
    {
      //Works with both lists
    }
}

Now, I want to make this class thread safe, and there are 2 different scenarios I see using locks, first to create

object _synchObject = new object()

and lock this object in all methods,
or second to lock each list when it is being used,
My first question is , which method is preferred?
and in case of second is preferred, is it performance issue to use lock like this?

    void MethodThatUsesBoth()
    {
       lock(aList){
           lock(bList){
                 //Works with both lists
           }
       }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你如我软肋 2024-10-16 02:02:36

这里有两个单独的问题:

  • 是否使用一个组合锁或两个单独的锁
  • 是否使用单独的对象来锁定列表本身(或它们的 SyncRoot)

这些在某种程度上是可分离的 - 在如果您使用两个单独的锁,则可以创建两个单独的对象来锁定,每个列表一个。

如果您希望 MethodThatUsesAList 和 MethodThatUsesBList 能够同时运行,则需要两个单独的锁。然后,您必须确保任何时候您可能获取两个锁时,都以相同的顺序获取它们。这意味着要对获取锁的任何方法中的所有代码进行推理:例如,您需要确保它不会调用获取另一个锁的另一个方法。

如果您的特定场景不太可能因所有方法被运行任何其中任何一个线程而有效地阻止其他线程而受到太大影响,那么我会使用单锁只是为了简单起见。

无论哪种情况,我个人都会选择其他代码不知道的“私有”锁。我发现以这种方式编写的代码更容易推理。使用列表本身或同步根可能绝对没问题 - 但我只是更喜欢考虑其他任何东西都无法获取的锁。

There are two separate questions here:

  • Whether to use one combined lock or two separate locks
  • Whether to use separate objects to lock on or the lists themselves (or their SyncRoots)

These are separable to some extent - in that if you're using two separate locks you can create two separate objects to lock on, one for each list.

If you want MethodThatUsesAList and MethodThatUsesBList to be able to run concurrently, you'll need two separate locks. You'll then have to make sure that any time you might acquire both locks, you acquire them in the same order. That means reasoning about all the code within any method which acquires a lock: you'll need to make sure that it doesn't call another method which acquires the other lock, for example.

If your particular scenario isn't likely to suffer much from all the methods effectively being blocked for other threads by any one thread running any of them, then I'd use a single lock just for simplicity.

In either case, I'd personally go for "private" locks which no other code knows about. I find it easier to reason about code written that way. Using the list itself or the sync root may well be absolutely fine - but I just prefer to think about locks which nothing else can possibly acquire.

请别遗忘我 2024-10-16 02:02:36
void MethodThatUsesBoth()
{
   lock(((ICollection)aList).SyncRoot){
       lock(((ICollection)bList).SyncRoot){
             //Works with both lists
       }
   }
}
void MethodThatUsesAList()
{
   lock(((ICollection)aList).SyncRoot){

   }
}
void MethodThatUsesBList()
{
   lock(((ICollection)bList).SyncRoot){

   }
}

在您的其他解决方案中,我认为这不好,因为当您仅在 aList 上工作时,您会阻止对 bList 的访问。这不是一个好的性能解决方案。

void MethodThatUsesBoth()
{
   lock(((ICollection)aList).SyncRoot){
       lock(((ICollection)bList).SyncRoot){
             //Works with both lists
       }
   }
}
void MethodThatUsesAList()
{
   lock(((ICollection)aList).SyncRoot){

   }
}
void MethodThatUsesBList()
{
   lock(((ICollection)bList).SyncRoot){

   }
}

In other your solution, i think is this not good because you blocking access to bList when you working only on aList. This is not good performance solution.

夏花。依旧 2024-10-16 02:02:36

一般来说,我认为前者(否则未使用的互斥锁)是首选。这样做没有真正的成本,而且很容易让后者出错,太容易造成僵局。

In general the former (an otherwised unused mutex) is preferred I believe. No real cost to do so, and too easy to get the latter wrong, too easy to create deadlocks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文