Начал изучать замок и сразу возник вопрос.
Здесь docs.microsoft говорится:
Оператор блокировки получает блокировку взаимного исключения для данного объект, выполняет блок операторов, а затем снимает блокировку. Пока замок удерживается, поток, который удерживает замок, может снова получить и отпустите замок. Любой другой поток не может получить блокировку и ждет, пока блокировка будет снята.
Я сделал простой пример, доказывающий, что другой поток с методом без ключевого слова lock может легко изменить данные экземпляра, в то время как этот экземпляр занят методом, использующим блокировку из первого потока. Стоит снять комментарий с блокировки и работа идет как положено. Я думал, что блокировка заблокирует доступ к экземпляру из других потоков, даже если они не используют блокировку этого экземпляра в своих методах.
Вопросы :
Правильно ли я понимаю, что блокировка экземпляра в одном потоке позволяет изменять данные из другого потока в этом экземпляре, если только этот другой поток также не использует блокировку этого экземпляра? Если да, то что тогда вообще дает такая блокировка и почему она делается именно так?
Что это значит, говоря простым языком? Пока блокировка удерживается, поток, удерживающий блокировку, может снова получить и снять блокировку.
Так что форматирование кода работает хорошо.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class A
{
public int a;
}
class Program
{
static void Main(string[] args)
{
A myA = new A();
void MyMethod1()
{
lock (myA)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
myA.a += 1;
Console.WriteLine($"Work MyMethod1 a = {myA.a}");
}
}
}
void MyMethod2()
{
//lock (myA)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
myA.a += 100;
Console.WriteLine($"Work MyMethod2 a = {myA.a}");
}
}
}
Task t1 = Task.Run(MyMethod1);
Thread.Sleep(100);
Task t2 = Task.Run(MyMethod2);
Task.WaitAll(t1, t2);
}
}
}
2 ответа
Блокировки являются совместными, он полагается на то, что все стороны, которые могут изменить данные, сотрудничают и берут блокировку, прежде чем пытаться изменить данные. Обратите внимание, что замку все равно, что вы изменяете внутри замка. Довольно часто для защиты некоторой структуры данных используется суррогатный объект блокировки. т.е.
private object myLockObject = new object();
private int a;
private int b;
public void TransferMonety(int amount){
lock(myLockObject){
if(a > amount){
a-=amount;
b+=amount;
}
}
}
Из-за этого блокировки очень гибкие, вы можете защитить любую операцию, но вам нужно правильно написать свой код.
Из-за этого важно быть осторожным при использовании замков. Блокировки предпочтительно должны быть закрытыми, чтобы избежать захвата блокировки каким-либо несвязанным кодом. Код внутри блокировки должен быть достаточно коротким и не должен вызывать какой-либо код вне класса. Это делается для того, чтобы избежать тупиковых ситуаций, если запущен произвольный код, он может делать такие вещи, как захват других блокировок или ожидание событий.
Хотя блокировки очень полезны, существуют и другие примитивы синхронизации, которые можно использовать в зависимости от вашего варианта использования.
Что это значит, говоря простым языком? "Пока блокировка удерживается, поток, удерживающий блокировку, может снова получить и снять блокировку".
Значит, вы можете это сделать:
lock (locker)
{
lock (locker)
{
lock (locker)
{
// Do something while holding the lock
}
}
}
Вы можете получить блокировку много раз, а затем снять ее столько же раз. Это называется повторным входом. Оператор lock
является реентерабельным, поскольку лежащий в его основе Класс Monitor
является реентерабельным по своей природе. Другие примитивы синхронизации, такие как SemaphoreSlim
, не реентерабельный.
Похожие вопросы
Связанные вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.
MyMethod1
) один человек получает ключ, открывает дверь, гладит кошку, а затем возвращает ключ. Без запирания (MyMethod2
) у вас нет ни двери, ни ключа. Вы можете просто пойти и погладить кошку, даже когда другие входят в дверь, потому что вас ничто не сдерживает. При блокировке вы не блокируете саму кошку — вы блокируете доступ к ней.why is it done this way?
Потому что именно так был разработан язык. Теоретически язык можно спроектировать таким образом, что если вы заблокируете поле в одном месте, оно будет автоматически заблокировано везде, где оно используется, но я не знаю ни одного языка, который делает это.