c#锁定传递给方法的引用 - 不好的做法?

gho*_*ago 16 .net c# multithreading locking thread-safety

我有一个类似的方法:

public static void DoSomething (string param1, string param2, SomeObject o) 
{
   //.....

   lock(o) 
   {
       o.Things.Add(param1);
       o.Update();
       // etc....
   }
}
Run Code Online (Sandbox Code Playgroud)

几点:

  1. 用这种方式锁定不好的做法?
  2. 我应该锁定private static object吗?
  3. 如果是这样,为什么?

Hei*_*nzi 21

为了最大限度地减少副作用,被锁定的对象不应该是被操纵的对象,而应该是指定用于锁定的单独对象.

根据您的要求,有几个选项可以解决此问题:

变体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....
   }
}
Run Code Online (Sandbox Code Playgroud)

变体B:将锁定对象作为参数传递

如果访问o必须是线程安全的,即使在外部DoSomething,即,如果有可能存在其他人编写DoSomethingElse并行运行DoSomething且不得干扰该lock块的方法的情况,请选择此选项DoSomething:

public static void DoSomething (string param1, string param2, SomeObject o, object someObjectLock) 
{
   //.....

   lock(someObjectLock) 
   {
       o.Things.Add(param1);
       o.Update();
       // etc....
   }
}
Run Code Online (Sandbox Code Playgroud)

变体C:创建SyncRoot属性

如果您可以控制实现SomeObject,则可以方便地将锁定对象作为属性提供.这样,您可以实现Variant B而无需传递第二个参数:

class SomeObject
{
    private readonly object syncRoot = new object();

    public object SyncRoot { get { return syncRoot; } }

    ...
}
Run Code Online (Sandbox Code Playgroud)

然后,您只需使用lock(o.SyncRoot)DoSomething.这是一些BCL类使用的模式,例如,Array.SyncLock,ICollection.SyncRoot.

  • 我相信`doSomethingLock`通常被称为`syncRoot`. (2认同)