锁定属性,好方法?

时间:2009-10-23 09:47:17

标签: c# winforms multithreading

在我的多线程应用程序中,我使用的是一些可以在同一时间被许多实例更改的变量。这很奇怪,但它没有任何问题,但工作正常。但我当然需要让它具有线程安全性。我刚开始使用锁,所以我会建议你的建议:

当客户端连接时,会创建类Client,其中每个客户端都有自己的“A”变量。

有时,客户端会调用类似的方法:

Client selectedClient SelectOtherClientClassByID(sentID);

selectedClient.A=5;

直到现在还没有问题,即使5个类同时进行(线程池),但我在考虑如何为A属性添加锁?

像:

A {
    get { return mA; }
    set {
        // use lock here for settting A to some value
    }    
}

会没事吗?

3 个答案:

答案 0 :(得分:16)

你需要在BOTH get和set中使用锁。此锁必须是同一个对象。例如:

private object mylock = new object();

public int A {

  get {
    int result;
    lock(mylock) {
    result = mA; 
    }
    return result;
  } 

  set { 
     lock(mylock) { 
        mA = value; 
     }
  }
}

答案 1 :(得分:6)

锁定对访问者内部属性的访问可能会导致虚假结果。例如,请查看以下代码:

class C {
    private object mylock = new object();

    public int A {

      get {
        int result;
        lock(mylock) {
        result = mA; 
        }
        return result;
      } 

      set { 
         lock(mylock) { 
            mA = value; 
         }
      }
    }
}
C obj = new C;
C.A++;

(是的,我从第一个答案中复制了它) 这里有竞争条件!操作“C.A ++”实际上需要对A进行两次单独访问,一次获取值,另一次设置更新值。没有什么可以确保这两个访问将一起执行,而不需要在它们之间进行上下文切换。竞争条件的经典场景!

所以,要小心!把锁放在访问器中并不是一个好主意,应该明确获得锁,就像前面的答案所说的那样(虽然它不一定是SyncRoots,任何对象都可以)

答案 2 :(得分:2)

当你需要的只是设置一个属性时,这是非常罕见的。更常见的是selectedClient.A = 5将成为更大的逻辑操作的一部分,其涉及若干任务/评估/等。在整个操作期间,您宁愿优先selectedClient处于一致状态而不是引入死锁/竞争条件。因此,在SyncRoot类中公开Client属性并从调用代码中锁定该属性会好得多:

Client selectedClient = GetClient(...);

lock(selectedClient.SyncRoot)
{
    selectedClient.A = 5;
    selectedClient.B = selectedClient.A * 54;
}