【问题标题】:Should I use a different object to lock for each property?我应该使用不同的对象来锁定每个属性吗?
【发布时间】:2012-11-28 05:20:58
【问题描述】:

我有一个类及其属性;对这些属性的访问非常频繁由不同的线程完成。

为每个属性使用相同的对象(用于锁定语句)更有效?

private readonly object padlock = new object();

private string name;
private int age;

public string Name
{
   get
   {
      lock(padlock)
         return name;
   }

   set
   {
      lock(padlock)
         name = value;
   }
}

public int Age
{
   get
   {
      lock(padlock)
         return age;
   }

   set
   {
      lock(padlock)
         age = value;
   }
}

或者为每个属性使用不同的对象?

private readonly object padlockName = new object();
private readonly object padlockAge = new object();

private string name;
private int age;

public string Name
{
   get
   {
      lock(padlockName)
         return name;
   }

   set
   {
      lock(padlockName)
         name = value;
   }
}

public int Age
{
   get
   {
      lock(padlockAge)
         return age;
   }

   set
   {
      lock(padlockAge)
         age = value;
   }
}

第二个版本有意义吗?

【问题讨论】:

  • 这取决于 - 您的属性是否链接在一起,不能同时读取和更改?
  • @MarcinJuraszek 否,但问题是如果一个线程正在读取/写入 Name 属性,则另一个线程必须等待才能读取/写入 Age 属性。这个等待时间(由于使用相同的对象)是相对较高还是使用相同的对象更好?
  • @LightStriker a) 如果类型是值类型,那么锁是没有价值的,由于装箱,你每次都会得到一个新的object b) 你打破封装,因为同一个对象(在string) 之类的引用类型的情况将被传出类,有人可以锁定它,然后导致代码死锁。
  • @Nick 我们无法回答这个问题,只有你可以。它强烈地基于类的使用方式。即使你也很难从概念上弄清楚。唯一实用的方法是对两者进行编码并分析结果。
  • @Servy 不幸的是我认为你是对的..

标签: c# multithreading locking


【解决方案1】:

我什至不愿回答您提出的问题,因为我怀疑这些锁定模式中的任何一种都可以确保您的应用程序的正确性。他们不确保对象作为一个整体保持一致的状态——他们只是确保各个属性不会同时更新。换句话说,您已经以迂回的方式实现了原子读写。

例如,假设您有一个将增加Age 的操作。如果两个不同的线程同时执行此操作,则最终结果可能是 (Age + 1) 或 (Age + 2)。

您很可能应该从对象中移除锁定,并让调用者酌情处理并发问题。一种简单的解决方案是在与对象交互期间锁定整个对象。例如:

lock(myObj){
  myObj.Age++;
  myObj.Name = "Bill";
}

更新

为了扩展我的中间段落,在两个不同线程上运行Age++ 可能会产生不同结果的原因是因为++ 运算符不是原子的。大致相当于这个。

int temp = Age;
temp = temp + 1;
Age = temp;

如果两个线程运行相同的东西,它可以像这样按顺序执行(为了清楚起见,我更改了临时变量的名称):

int temp1 = Age; //thread 1
int temp2 = Age; //thread 2
temp1 = temp1 + 1;  //thread 1
temp2 = temp2 + 1; //thread 2
Age = temp1; //thread 1
Age = temp2; //thread 2

锁定的目的是确保一个线程在另一个线程之前运行整个读-增量-写序列。但是您的锁定方案并没有做到这一点。

【讨论】:

  • 这个答案的第一句话很重要。 +1
  • 我不明白。如果 2 个不同的线程增加 Age 最终结果将是 Age + 2:如果这是我想要的,这个结果有什么奇怪的?并且每次锁定 myOby 并不会提高性能。你能详细说明一下吗?
【解决方案2】:

注意,这在很大程度上取决于您的对象是如何被访问的。最终答案将通过衡量您在您的环境中的表现并采取行动以及特定于您的域的需求来确定(尽管您在这里的担忧似乎正在破坏用于支持属性的变量,而不是同时访问属性集群)。

也就是说你必须平衡以下几点:

  • 如果您有一个对象来锁定对对象上所有属性的访问,则访问不同属性的多个线程将相互等待完成。

  • 如果你有一个对象每个属性,那么访问不同的属性不会导致锁等待。

但是,我想说在这种情况下它没有实际意义,因为您似乎更担心这里的值损坏。

Assignments and reads on a vast majority of types (excluding multi-field value types) are atomic,这意味着你不会破坏它们。

也就是说,在上面的示例中,您可以读取和写入类型为 string(引用类型)和 int 的属性,这些操作是原子的,不会被从多个线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 2017-08-27
    相关资源
    最近更新 更多