5

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

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

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

第二个版本有意义吗?

4

2 回答 2

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

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

于 2012-11-27T21:00:56.563 回答
0

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

也就是说,您必须平衡以下内容:

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

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

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

绝大多数类型(不包括多字段值类型)的赋值和读取都是原子的,这意味着您不会破坏它们。

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

于 2012-11-27T20:57:32.873 回答