Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Оператор 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#.