Будет ли оператор lock() блокировать все потоки в процессе/appdomain?

Возможно, вопрос звучит глупо, но я не понимаю "что-то о потоках и блокировке", и я хотел бы получить подтверждение (здесь, почему я спрашиваю).

Итак, если у меня есть 10 серверов и 10 запросов одновременно, приходите на каждый сервер, то 100 запросов по ферме. Без блокировки, то 100 запросов к базе данных.

Если я сделаю что-то вроде этого:

private static readonly object myLockHolder = new object();
if (Cache[key] == null)
{
   lock(myLockHolder)
   {
      if (Cache[key] == null)
      {
         Cache[key] = LengthyDatabaseCall();
      }
   }
}

Сколько запросов на базу данных я сделаю? 10? 100? Или сколько у меня есть потоков?

Ответ 1

У вас есть иерархия объектов:

  • У вас есть серверы (10)
  • На каждом сервере у вас есть процессы (возможно, только 1 - ваш пул служб/приложений)
  • В каждом процессе у вас есть потоки (возможно, многие)

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

Использование оператора lock фактически не блокирует нити. Однако, если один поток выполняет код внутри блокировки (то есть в блоке кода, следующем за оператором lock), любой другой поток, который хочет взять блокировку и выполнить тот же код, должен ждать, пока первый поток, содержащий блокировку оставляет блок кода и освобождает блокировку.

Оператор С# lock использует критический раздел Windows , который представляет собой легкий механизм блокировки. Если вы хотите заблокировать процессы, вы можете вместо этого использовать mutex. Для блокировки серверов вы можете использовать базу данных или общий файл.

Как отметил dkackman,.NET имеет концепцию AppDomain, которая является своего рода легким процессом. Вы можете иметь несколько приложений для каждого процесса. Оператор С# lock блокирует только ресурс в пределах одного AppDomain, и правильное описание иерархии будет включать AppDomain под процесс и над потоками. Однако довольно часто у вас есть только один AppDomain в процессе, делающем различие несколько несущественным.

Ответ 2

Оператор С# lock блокирует определенный экземпляр объекта (объект, созданный с помощью new object()). Объекты (в большинстве случаев) не передаются через AppDomains, поэтому, если у вас есть 10 серверов, 10 потоков могут запускать одновременно доступ к вашей базе данных с помощью этого фрагмента кода.

Ответ 3

Блокировка не блокирует потоки. Он блокирует некоторый экземпляр объекта. И каждый поток, который пытается получить к нему доступ, блокируется. Поэтому в вашем случае каждый поток, который попытается получить доступ к myLockHolder, будет заблокирован, а не все потоки. Другими словами, мы можем сказать, что Оператор блокировки является синтаксическим сахаром для использования Критический раздел.

Как вы можете видеть в MSDN:

блок блокировки (выражение)

где:

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

блок оператора Операторы критического раздела.

Ответ 4

lock заблокирует все потоки в этом приложении от доступа к объекту myLockHolder.

Итак, если у вас есть 10 экземпляров приложения, вы получите 10 запросов к серверу, пока объект заблокирован для каждого. Когда вы выйдете из оператора блокировки, следующий запрос будет обрабатываться в этом приложении, но пока Cache[key] не null, он не получит доступ к базе данных.

Количество действительных запросов зависит от того, что происходит здесь:

  if (Cache[key] == null)
  {
     Cache[key] = LengthyDatabaseCall();
  }

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

Ответ 5

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

сколько из того, что есть в любое время, трудно определить.

Ответ 6

Ваша БД получит 10 запросов, причем вероятность того, что запросы 2-10 выполняются намного быстрее, чем запрос 1.