c#锁定传递给方法的引用 – 不好的做法?
我有一个类似的方法:
public static void DoSomething (string param1, string param2, SomeObject o) { //..... lock(o) { o.Things.Add(param1); o.Update(); // etc.... } }
几点:
- 用这种方式锁定不好的做法?
- 我应该锁定
private static object
吗? - 如果是这样,为什么?
为了最大限度地减少副作用,被锁定的对象不应该是被操纵的对象,而应该是指定用于锁定的单独对象。
根据您的要求,有几个选项可以解决此问题:
变体A:私人锁定对象
如果您只想确保DoSomething
不与DoSomething
的并行实例冲突,请选择此选项。
private static readonly object doSomethingLock = new object(); public static void DoSomething (string param1, string param2, SomeObject o) { //..... lock(doSomethingLock) { o.Things.Add(param1); o.Update(); // etc.... } }
变体B:将锁定对象作为参数传递
如果访问o
必须在DoSomething
之外是线程安全的,那么选择此选项,即,如果存在其他人编写与DoSomethingElse
并行运行且不得干扰DoSomething
的lock
块的方法DoSomethingElse
的可能性:
public static void DoSomething (string param1, string param2, SomeObject o, object someObjectLock) { //..... lock(someObjectLock) { o.Things.Add(param1); o.Update(); // etc.... } }
变体C:创建SyncRoot属性
如果您可以控制SomeObject
的实现,则可以方便地将锁定对象作为属性提供。 这样,您可以实现Variant B而无需传递第二个参数:
class SomeObject { private readonly object syncRoot = new object(); public object SyncRoot { get { return syncRoot; } } ... }
然后,您只需在DoSomething
使用lock(o.SyncRoot)
。 这是一些BCL类使用的模式,例如, Array.SyncLock , ICollection.SyncRoot 。
只是回答你的第三个问题:
想象一下,后者你决定锁定另一个方法参数,可能是这样的:
public void XXX(object o) { lock(o) { } }
您将很难尝试查看是否存在死锁。 您将需要检查作为参数传递给SomeObject的对象是否永远不会作为参数传递给对象o。
这是一个关于如何使用锁的示例:
class Account { decimal balance; private Object thisLock = new Object(); public void Withdraw(decimal amount) { lock (thisLock) { if (amount > balance) { throw new Exception("Insufficient funds"); } balance -= amount; } } }
这意味着您锁定一个私有变量的Object,仅用于锁定而不是其他任何东西,
你可以看看这个:
lock语句(C#参考)
和
线程同步(C#和Visual Basic)