Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Оператор lock получает блокировку взаимного исключения для данного объекта, выполняет блок инструкции, а затем освобождает блокировку. Хотя блокировка удерживается, поток, содержащий блокировку, может получить и освободить блокировку несколько раз. Любой другой поток заблокирован от получения блокировки и ожидает, пока блокировка не будет освобождена. Оператор lock гарантирует, что в любой момент времени выполняется только один поток.
Справочные документы на языке C#, выпущенные последней версией языка C#. Она также содержит начальную документацию по функциям в общедоступных предварительных версиях для предстоящего языкового выпуска.
Документация определяет любую функцию, впервые представленную в последних трех версиях языка или в текущих общедоступных предварительных версиях.
Подсказка
Чтобы узнать, когда функция впервые появилась в C#, ознакомьтесь со статьей по журналу версий языка C#.
Оператор lock принимает следующую форму:
lock (x)
{
// Your code...
}
Переменная x — это выражение System.Threading.Lock типа или ссылочного типа. Когда компилятор знает, что x имеет тип System.Threading.Lock, это точно эквивалентно:
using (x.EnterScope())
{
// Your code...
}
Объект, возвращаемый Lock.EnterScope() методомref struct, является Dispose() объектом, который включает метод. Созданная using инструкция гарантирует, что область освобождается, даже если исключение создается в тексте lock инструкции.
lock В противном случае оператор точно эквивалентен следующему:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Так как код использует try-finally инструкцию, блокировка освобождается даже в том случае, если исключение создается в тексте lock инструкции.
Выражение в тексте инструкции await нельзя использоватьlock.
Руководящие принципы
Начиная с .NET 9 и C# 13, блокировка выделенного экземпляра объекта типа для оптимальной System.Threading.Lock производительности. Компилятор также выдает предупреждение, если вы приведете известный Lock объект к другому типу и заблокируете его. Если вы используете более раннюю версию .NET и C#, заблокируйте экземпляр выделенного объекта, который не используется для другой цели. Избегайте использования одного экземпляра объекта блокировки для разных общих ресурсов, так как это может привести к взаимоблокировке или конфликту блокировки. В частности, избегайте использования следующих экземпляров в качестве объектов блокировки:
-
this, как вызывающие также могут блокироватьсяthis. - Type экземпляры, так как они могут быть получены оператором typeof или отражением.
- экземпляры строк, включая строковые литералы, так как они могут быть интернированы.
Удерживайте блокировку на максимально короткое время, чтобы уменьшить конфликт блокировки.
Пример
В следующем примере определяется класс, который синхронизирует Account доступ к своему частному balance полю путем блокировки выделенного balanceLock экземпляра. Использование одного и того же экземпляра для блокировки гарантирует, что два разных потока не могут обновлять balance поле, вызывая Debit методы или Credit методы одновременно. В примере используется C# 13 и новый Lock объект. Если вы используете более раннюю версию C# или более старую библиотеку .NET, заблокируйте экземпляр object.
using System;
using System.Threading.Tasks;
public class Account
{
// Use `object` in versions earlier than C# 13
private readonly System.Threading.Lock _balanceLock = new();
private decimal _balance;
public Account(decimal initialBalance) => _balance = initialBalance;
public decimal Debit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The debit amount cannot be negative.");
}
decimal appliedAmount = 0;
lock (_balanceLock)
{
if (_balance >= amount)
{
_balance -= amount;
appliedAmount = amount;
}
}
return appliedAmount;
}
public void Credit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The credit amount cannot be negative.");
}
lock (_balanceLock)
{
_balance += amount;
}
}
public decimal GetBalance()
{
lock (_balanceLock)
{
return _balance;
}
}
}
class AccountTest
{
static async Task Main()
{
var account = new Account(1000);
var tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => Update(account));
}
await Task.WhenAll(tasks);
Console.WriteLine($"Account's balance is {account.GetBalance()}");
// Output:
// Account's balance is 2000
}
static void Update(Account account)
{
decimal[] amounts = [0, 2, -3, 6, -2, -1, 8, -5, 11, -6];
foreach (var amount in amounts)
{
if (amount >= 0)
{
account.Credit(amount);
}
else
{
account.Debit(Math.Abs(amount));
}
}
}
}
Спецификация языка C#
Дополнительные сведения см. в разделе инструкции блокировкиспецификации языка C#.