Возможно, вопрос звучит глупым, но я не понимаю 'что-то о потоках и блокировке, и я хотел бы добраться, подтверждение (вот то, почему я спрашиваю).
Так, если у меня есть 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? Или так же, как у меня есть потоки?
У вас есть иерархия объектов:
Ваш код будет только запрещать потокам в рамках одного процесса на одном сервере доступ для одновременного изменения объекта Cache
. Вы можете создавать блокировки между процессами и даже между серверами, но стоимость значительно возрастает по мере продвижения вверх по иерархии.
Использование оператора lock
фактически не блокирует какие-либо потоки. Однако, если один поток выполняет код внутри блокировки (то есть в блоке кода, следующем за оператором lock
), любой другой поток, который хочет взять блокировку и выполнить тот же код, должен дождаться первого поток, удерживающий блокировку, покидает блок кода и снимает блокировку.
Оператор C # lock
использует критическую секцию Windows , которая представляет собой облегченный механизм блокировки. Если вы хотите заблокировать процессы, вы можете вместо этого использовать мьютекс . Для блокировки серверов вы можете использовать базу данных или общий файл.
Как заметил Дкакман, .NET имеет концепцию AppDomain, которая представляет собой своего рода облегченный процесс. У вас может быть несколько доменов приложений для каждого процесса. Оператор C # lock
блокирует только ресурс в пределах одного AppDomain, и правильное описание иерархии будет включать AppDomain ниже процесса и выше потоков. Однако довольно часто у вас есть только один домен приложения в процессе, что делает различие в некотором роде несущественным.
Оператор C # lock
блокирует конкретный экземпляр объекта (объект, созданный с помощью new object ()
). Объекты (в большинстве случаев) не разделяются между доменами приложений, поэтому, если у вас 10 серверов, 10 потоков могут одновременно работать с вашей базой данных с помощью этого фрагмента кода.
Блокировка не блокирует потоки. Он блокирует некоторый экземпляр объекта. И каждый поток, который пытается получить к нему доступ, блокируется. Таким образом, в вашем случае каждый поток, который попытается получить доступ к myLockHolder, будет заблокирован, а не все потоки. Другими словами, мы можем сказать, что Оператор Lock является синтаксическим сахаром для использования Critical Section .
Как вы можете видеть в MSDN:
блок операторов блокировки (выражение)
где:
выражение Определяет объект, который вы хотите заблокировать. выражение должно быть ссылочным типом. Обычно выражение будет либо это, если вы хотите защитить переменную экземпляра, или typeof (class), если вы хотите защитить статическую переменную (или если критическая секция возникает в статике метод в данном классе).
блок операторов операторы критического раздела.
lock
заблокирует доступ всех потоков этого приложения к объекту myLockHolder.
Таким образом, если у вас запущено 10 экземпляров приложения, вы получите 10 запросов к серверу, пока объект заблокирован на каждом из них. Как только вы выйдете из оператора блокировки, следующий запрос будет обрабатываться в этом приложении, но пока Cache[key]
не null
, он не получит доступ к базе данных...
Количество фактических запросов, которые вы получите, зависит от того, что произойдет здесь:
if (Cache[key] == null)
{
Cache[key] = LengthyDatabaseCall();
}
Если LengthyDatabaseCall();
потерпит неудачу, следующий запрос попытается обратиться к серверу базы данных и также получить информацию, так что в лучшем случае будет только 10 запросов к серверу.
Только те потоки, которым нужен доступ к вашей общей переменной в тот момент, когда другой поток использует ее, перейдут в состояние ожидания.
Сколько их будет в каждый момент времени, определить сложно.
Ваша БД получит 10 запросов, с большой вероятностью, что запросы 2-10 будут выполняться намного быстрее, чем запрос 1.