Выбор ответа Middle Потоки и синхронизация спрашивают часто обновлено

await внутри lock

Вопрос

Почему такой код не компилируется и что использовать вместо него?

private readonly object _sync = new();

public async Task SaveAsync()
{
    lock (_sync)
    {
        await _db.SaveChangesAsync();
    }
}

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Монитор привязан к потоку, а после await поток другой: SemaphoreSlim. Monitor владеет потоком, который захватил блокировку.

Варианты ответа

  1. lock нельзя использовать в async-методах вообще, даже без await
  2. Нужно заменить lock на Monitor.Enter, тогда await разрешён
  3. Нужно сделать _sync статическим полем, тогда монитор общий
  4. Монитор привязан к потоку, а после await поток другой: SemaphoreSlimправильный ответ

Разбор

Monitor владеет потоком, который захватил блокировку. После await продолжение может попасть на другой поток, и освобождение от его имени невозможно, поэтому компилятор запрещает await в теле lock (CS1996). lock в async-методе допустим, если внутри нет await. Monitor.Enter вручную даёт ту же проблему, только без защиты компилятора. Асинхронный аналог: SemaphoreSlim(1, 1) с await WaitAsync() и Release() в finally.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме