Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Lock в C#
Lock — это синхронизационный механизм в C#, который используется для обеспечения потокобезопасности при работе с общими ресурсами в многопоточном приложении. Он гарантирует, что только один поток может одновременно выполнять критическую секцию кода.
Как работает Lock
var lockObj = new object();
lock (lockObj)
{
// Критическая секция
// Только один поток может здесь находиться
// Другие потоки ждут, пока текущий поток выйдет из блока
}
Проблема без Lock
public class BankAccount
{
public decimal Balance { get; set; }
public void Withdraw(decimal amount)
{
if (Balance >= amount)
{
Thread.Sleep(100); // имитация задержки
Balance -= amount;
}
}
}
// Использование
var account = new BankAccount { Balance = 1000 };
var tasks = new List<Task>();
// 10 потоков пытаются снять по 200
for (int i = 0; i < 10; i++)
{
tasks.Add(Task.Run(() => account.Withdraw(200)));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine($"Balance: {account.Balance}");
// Ожидаемо: 0
// На самом деле: может быть 600, 800 или другое значение!
// Это Race Condition
Решение с Lock
public class BankAccount
{
private readonly object _lockObj = new object();
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
lock (_lockObj)
{
if (Balance >= amount)
{
Thread.Sleep(100);
Balance -= amount;
Console.WriteLine($"Снято {amount}, остаток: {Balance}");
}
else
{
Console.WriteLine($"Недостаточно средств");
}
}
}
}
Варианты синхронизации
ReaderWriterLockSlim для чтения/записи:
private readonly ReaderWriterLockSlim _rw = new();
private decimal _balance = 1000;
public decimal GetBalance()
{
_rw.EnterReadLock();
try { return _balance; }
finally { _rw.ExitReadLock(); }
}
SemaphoreSlim для асинхронного кода:
private readonly SemaphoreSlim _semaphore = new(1);
public async Task WithdrawAsync(decimal amount)
{
await _semaphore.WaitAsync();
try { Balance -= amount; }
finally { _semaphore.Release(); }
}
Interlocked для простых операций:
private int _counter = 0;
Interlocked.Increment(ref _counter);
Interlocked.Add(ref _counter, 5);
Лучшие практики
- Минимизируй время в lock — выполняй только необходимый код
- Используй private readonly object для lockObj
- Избегай вложенных locks — риск дедлока
- Для async используй SemaphoreSlim вместо lock
- Профилируй узкие места — lock может быть bottleneck
- Предпочитай Interlocked операции для простых случаев
Дедлок пример
var lock1 = new object();
var lock2 = new object();
// Thread 1
lock (lock1)
{
Thread.Sleep(100);
lock (lock2) { } // Ждет lock2
}
// Thread 2
lock (lock2)
{
Thread.Sleep(100);
lock (lock1) { } // Ждет lock1 — ДЕДЛОК!
}
Lock — это базовый инструмент для синхронизации потоков, но требует аккуратности в использовании. Для сложных сценариев рассмотри более высокоуровневые механизмы как async/await, TPL (Task Parallel Library) или использование thread-safe коллекций.