Почему этот код может уйти в deadlock, когда A держит RLock и вызывает B, берущий write-Lock?
A берёт read-блокировку и вызывает B, который берёт write-блокировку на том же RWMutex. Под нагрузкой это может уйти в deadlock.
var mu sync.RWMutex
func A() {
mu.RLock()
defer mu.RUnlock()
B()
}
func B() {
mu.Lock()
defer mu.Unlock()
}
Найдите ошибку и объясните причину.
RWMutex в Go не реентрантен. A держит read-блокировку и вызывает B, который запрашивает write-Lock. Если другая горутина уже ждёт Lock, runtime блокирует новых читателей, чтобы избежать голодания писателя — поэтому B ждёт read-блокировку, которую всё ещё держит A. Документация запрещает рекурсивный RLock; берите блокировку один раз на верхнем уровне.
- ✗Считать
sync.RWMutexреентрантным — рекурсивное взятие может привести к deadlock - ✗Думать, что deadlock безусловен, а не вызван конкурентно ждущим писателем
- ✗Брать блокировку во вложенном вызове вместо одного взятия на верху цепочки вызовов
- →Почему
RWMutexблокирует новых читателей, как только писатель ждёт? - →Как перестроить
AиB, чтобы блокировку брал только один из них?
Найдите ошибку
var mu sync.RWMutex
func A() {
mu.RLock()
defer mu.RUnlock()
B()
}
func B() {
mu.Lock() // хочет write-блокировку
defer mu.Unlock()
}
Почему deadlock под нагрузкой
sync.RWMutex не реентрантен: горутина, держащая блокировку, не может взять её снова.
A берёт RLock и вызывает B, который запрашивает write-Lock. Сам по себе один такой стек мог бы пройти, но ловушка — в политике против голодания писателя:
Если хотя бы одна горутина уже ждёт
Lock(), последующиеRLock()блокируются, чтобы ждущий писатель в итоге получил доступ.
Под конкуренцией это смертельно: пока другая горутина ждёт Lock, B тоже встаёт на Lock, но не может его получить, пока A держит RLock — а A не отпустит RLock, пока не вернётся B. Круг замкнулся.
⚠️ Документация Go явно запрещает рекурсивный RLock. Исправление — не брать блокировку во вложенном вызове; берите её один раз на верхнем уровне:
func A() {
mu.Lock()
defer mu.Unlock()
bLocked() // версия B без собственной блокировки
}