Подтвердить что ты не робот

С# блокировка ссылки передана методу - плохая практика?

У меня есть метод, похожий на:

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

   lock(o) 
   {
       o.Things.Add(param1);
       o.Update();
       // etc....
   }
}

Несколько точек:

  • Является ли блокировка таким образом плохой практикой?
  • Следует ли вместо этого заблокировать private static object?
  • Если да, то почему?
4b9b3361

Ответ 1

Чтобы свести к минимуму побочные эффекты, объект, который заблокирован, не должен быть объектом манипулирования, а отдельным объектом, предназначенным для блокировки.

В зависимости от ваших требований существует несколько вариантов решения этой проблемы:

Вариант 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.

Ответ 2

Отвечая на ваш третий вопрос:

Представьте, что последний на вас решил заблокировать другой параметр метода, возможно, что-то вроде:

public void XXX(object o)
{
    lock(o)
    {

    }
}

Вам будет трудно найти, есть ли тупик. Вам нужно будет проверить, что объект, переданный как параметр SomeObject o, никогда не передается как параметр для объекта o одновременно.

Ответ 3

Вот пример того, как вы должны использовать lock:

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

И это означает, что вы блокируете объект, который является частной переменной и используется только для блокировки и ничего другого,

Вы можете посмотреть на это:

оператор блокировки (ссылка на С#)

и

Синхронизация потоков (С# и Visual Basic)