我应该使用不同的对象来锁定每个属性吗?

时间:2012-11-27 20:48:22

标签: c# multithreading locking

我有一个班级及其属性; 经常由不同的线程完成对这些属性的访问。

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

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

第二个版本有意义吗?

2 个答案:

答案 0 :(得分:4)

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

例如,假设您有一个增加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 :(得分:0)

请注意,这在很大程度上取决于对象的访问方式。最终的答案将通过衡量您在 环境中的表现并根据您的域的具体需求来确定(尽管您的担心似乎在于腐败)用于支持属性的变量,而不是用于同时访问属性的集群。)

那说你必须平衡以下几点:

  • 如果您有一个对象来锁定对该对象的属性 all 的访问权限,则访问不同属性的多个线程将等待彼此完成。

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

但是,我会说在这种情况下它没有实际意义,因为你似乎更担心这里价值观的腐败。

Assignments and reads on a vast majority of types (excluding multi-field value types) are atomic,意味着你不会腐蚀他们。

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