Чтобы свести к минимуму побочные эффекты, блокируемый объект должен быть не объектом, которым манипулируют, а отдельным объектом, предназначенным для блокировки.
В зависимости от ваших требований, есть несколько вариантов решения этой проблемы:
Вариант 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
в DoSomething
:
public static void DoSomething (string param1, string param2, SomeObject o, object someObjectLock)
{
//.....
lock(someObjectLock)
{
o.Things.Add(param1);
o.Update();
// etc....
}
}
Вариант C: создание свойства SyncRoot
Если у вас есть контроль над реализацией SomeObject
, может быть удобно предоставить объект блокировки как свойство. Таким образом, вы можете реализовать вариант B без необходимости передавать второй параметр:
class SomeObject
{
private readonly object syncRoot = new object();
public object SyncRoot { get { return syncRoot; } }
...
}
Тогда вы просто используете lock(o.SyncRoot)
в DoSomething
. Это шаблон, который используют некоторые классы BCL, например, Array.SyncLock , ICollection.SyncRoot .