Использование одной и той же блокировки для нескольких методов - PullRequest
20 голосов
/ 23 декабря 2010

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

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 также используется синглтоном (в свойстве Instance)?

Редактировать: Чтобы уточнить мой вопрос об использовании того же самого объекта блокировки в классе 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
      }  
}

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

Ответы [ 5 ]

16 голосов
/ 23 декабря 2010

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

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

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

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

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

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

8 голосов
/ 23 декабря 2010

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

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

5 голосов
/ 23 декабря 2010

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

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

Узкое место

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

2 голосов
/ 23 декабря 2010

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

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

0 голосов
/ 30 апреля 2019

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

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

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

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

...