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

Использование одной и той же блокировки для нескольких методов

У меня пока не было проблем с использованием одной и той же блокировки для нескольких методов, но мне интересно, могут ли на самом деле какие-то проблемы (производительность?), о которых я не знаю:

private static readonly object lockObj = new object();

public int GetValue1(int index)
{
    lock(lockObj)
    {
        // Collection 1 read and/or write
    }
}

public int GetValue2(int index)
{
    lock(lockObj)
    {
        // Collection 2 read and/or write
    }
}

public int GetValue3(int index)
{
    lock(lockObj)
    {
        // Collection 3 read and/or write
    }
}

3 метода и коллекции в любом случае не связаны.

Кроме того, будет ли проблема, если этот lockObj также используется одиночным (в свойстве экземпляра)?

Изменить: Чтобы уточнить мой вопрос об использовании одного и того же объекта блокировки в классе Singleton:

private static readonly object SyncObject = new object();

public static MySingleton Instance
{
    get
    {
        lock (SyncObject)
        {
          if (_instance == null)
          {
              _instance = new MySingleton();
          }
        }
        return _instance;
    }
}

public int MyMethod()
{
      lock (SyncObject)
      {
           // Read or write
      }  
}

Это вызовет проблемы?

4b9b3361

Ответ 1

Если методы не связаны со статусом, то для каждого из них используется другая блокировка; в противном случае это неэффективно (поскольку нет причин для разных методов блокировки одного и того же объекта, поскольку они могут безопасно выполнять одновременно).

Кроме того, похоже, что это блокировка экземпляров объектов на статическом объекте - это было предназначено? У меня такое чувство, что ошибка; методы экземпляра должны (обычно) блокировать только поля экземпляра.

Относительно шаблона проектирования Singleton:

В то время как блокировка может быть безопасна для них, лучшая практика делает задержанную инициализацию поля следующим образом:

private static object sharedInstance;
public static object SharedInstance
{
     get
     {
          if (sharedInstance == null)
              Interlocked.CompareExchange(ref sharedInstance, new object(), null);
          return sharedInstance;
     }
}

Таким образом, это немного быстрее (как из-за того, что блокированные методы выполняются быстрее, а потому, что инициализация задерживается), но по-прежнему небезопасная.

Ответ 2

Используя один и тот же объект для lock во всех этих методах, вы производите сериализацию всего доступа к коду во всех потоках.

То есть... code running GetValue1() блокирует другой код в другом потоке от запуска GetValue2() до его завершения. Если вы добавите еще больше кода, который блокируется на одном экземпляре объекта, в какой-то момент вы в конечном итоге получите однопоточное приложение.

Ответ 3

Общие блокировки блокируют другие не связанные вызовы

Если вы используете один и тот же замок, то блокировка одним способом также незаметно блокирует других. Если они не связаны вообще, это проблема, поскольку они должны ждать друг друга. Которые они не должны.

Узкое

Это может стать узким местом, когда эти методы часто вызывают. С отдельными замками они запускались независимо, но, используя один и тот же замок, это означает, что они должны ждать, пока блокировка будет выпущена чаще, чем требуется (на самом деле три раза).

Ответ 4

Чтобы создать потокобезопасный синглтон, используйте этот метод.
Вам не нужен замок.

В общем, каждый замок следует использовать как можно меньше.
Чем больше методов блокирует одно и то же, возможно, вам захочется в ожидании его, когда вам это действительно не нужно.

Ответ 5

Хороший вопрос. Есть плюсы и минусы в том, чтобы сделать блокировки более детализированными или более грубыми, причем одна крайность - это отдельная блокировка для каждого фрагмента данных, а другая - одна блокировка для всей программы. Как отмечают другие посты, недостатком повторного использования одних и тех же блокировок является то, что в общем случае вы можете получить меньше параллелизма (хотя это зависит от случая, вы не можете получить меньше параллелизма).

Однако недостаток использования большего числа блокировок состоит в том, что в общем случае вы делаете тупик более вероятным. Есть больше способов получить взаимоблокировки, чем больше замков вы задействовали. Например, одновременное получение двух блокировок в отдельных потоках, но в обратном порядке - это потенциальная тупиковая ситуация, которая не возникла бы, если бы была задействована только одна блокировка. Конечно, иногда вы можете устранить тупик, разбив одну блокировку на две, но обычно меньшее количество блокировок означает меньшее количество блокировок. Также добавлена сложность кода с наличием большего количества блокировок.

В целом эти два фактора должны быть сбалансированы. Обычно для удобства используется одна блокировка на класс, если это не вызывает проблем с параллелизмом. Фактически, это шаблон проектирования, называемый монитором.

Я бы сказал, что рекомендуется использовать меньшее количество блокировок для простоты кода и создавать дополнительные блокировки, если есть веская причина (например, параллелизм или случай, когда он более прост или устраняет тупик).