Я вижу, что для использования объектов, которые не являются потокобезопасными, мы обмениваем код блокировкой следующим образом:
private static readonly Object obj = new Object();
lock (obj)
{
// thread unsafe code
}
Итак, что происходит, когда несколько потоков обращаются к одному и тому же коду (предположим, что он запущен в веб-приложении ASP.NET). Они поставлены в очередь? Если да, то как долго они будут ждать?
Какое влияние влияет на использование блокировок?
Оператор lock
переведен на С# 3.0 следующим образом:
var temp = obj;
Monitor.Enter(temp);
try
{
// body
}
finally
{
Monitor.Exit(temp);
}
В С# 4.0 это изменилось, и теперь оно создается следующим образом:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
// body
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(temp);
}
}
Подробнее о том, что Monitor.Enter
делает здесь. Чтобы процитировать MSDN:
Используйте
Enter
для получения объект передается как параметр. Если другой поток выполнилEnter
на объект, но еще не выполнен соответствующийExit
, текущий поток будет блокироваться до другого поток освобождает объект. это легально для того же потока, чтобы вызватьEnter
более одного раза без него блокирование; однако, равное количество ВызовExit
должен быть вызван до другие потоки, ожидающие объекта разблокируется.
Метод Monitor.Enter
будет бесконечно ждать; он не будет тайм-аут.
Это проще, чем вы думаете.
Согласно Microsoft:
Ключевое слово lock
гарантирует, что один поток не войдет в критический раздел кода, а другой поток находится в критическом разделе. Если другой поток пытается ввести заблокированный код, он будет ждать, блокировать, пока объект не будет выпущен.
Ключевое слово lock
вызывает Enter
в начале блока и Exit
в конце блока. lock
ключевое слово на самом деле обрабатывает Monitor
класс в конце.
Например:
private static readonly Object obj = new Object();
lock (obj)
{
// critical section
}
В приведенном выше коде первая нить входит в критический раздел, тогда он блокирует obj
, и когда другой поток пытается ввести, тогда он также попытается заблокировать obj
, который уже заблокирован первым потоком, мне придется ждать первого для выпуска obj
. и когда первый выйдет, другой поток заблокирует obj
и войдет в критический раздел.
Нет, они не поставлены в очередь, они спали
Оператор блокировки формы
lock (x) ...
где x - выражение ссылочного типа, точно эквивалентно
var temp = x;
System.Threading.Monitor.Enter(temp);
try { ... }
finally { System.Threading.Monitor.Exit(temp); }
Вам просто нужно знать, что они ждут друг друга, и только один поток войдет в блокирующий блок, остальные будут ждать...
Монитор полностью написан в .net, поэтому достаточно быстро, также посмотрите класс Monitor с reflector для более подробной информации
lock
немного изменился в C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
Замки блокируют другие потоки от выполнения кода, содержащегося в блоке блокировки. Потокам придется подождать, пока поток внутри блока блокировки не завершится, и блокировка будет отпущена. Это оказывает негативное влияние на производительность в многопоточной среде. Если вам это нужно, вы должны убедиться, что код в блоке блокировки может обрабатываться очень быстро. Вы должны стараться избегать дорогостоящих действий, таких как доступ к базе данных и т.д.
Влияние производительности зависит от способа блокировки. Здесь вы можете найти хороший список оптимизаций: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
В основном вы должны попытаться заблокировать как можно меньше, так как он заставляет ваш ожидающий код спать. Если у вас есть тяжелые вычисления или длительный код (например, загрузка файла) в блокировке, это приводит к огромной потере производительности.
do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue
]. Самая большая опасность состоит в том, что если требования развиваются сверх того, что могут обрабатывать такие методы, может быть сложно адаптировать код для их обработки.
Оператор lock
преобразуется в вызовы методов Enter
и Exit
Monitor
.
Оператор lock
будет ждать неограниченное время для освобождения объекта блокировки.
Часть внутри оператора блокировки может выполняться только одним потоком, поэтому все остальные потоки будут бесконечно ждать, пока поток, удерживающий блокировку, не будет завершен. Это может привести к так называемому тупику.