Какие режимы работы существуют у `sync.Mutex` в Go и в чем их различие?

Ответ

sync.Mutex в Go имеет два режима работы, которые он переключает автоматически для оптимизации производительности и справедливости:

  1. Нормальный режим (Normal mode)

    • Когда используется: Когда мьютекс свободен или конкуренция за него невысока.
    • Как работает: Горутины, запрашивающие мьютекс, сначала пытаются захватить его с помощью атомарных операций (spin-loop) в течение короткого времени. Это очень быстро, если мьютекс освобождается почти сразу, и позволяет избежать затрат на переключение контекста горутины. Если захватить мьютекс не удалось, горутина уходит в ожидание.
    • Недостаток: В этом режиме возможна ситуация "захвата на опережение" (barging), когда новая горутина может перехватить мьютекс у горутины, которая уже давно стоит в очереди.
  2. Режим голодания (Starvation mode)

    • Когда используется: Мьютекс переходит в этот режим, если горутина ожидает освобождения дольше 1 миллисекунды.
    • Как работает: В этом режиме мьютекс передается строго по очереди — от освободившей его горутины к той, что дольше всех ждет в очереди. Новые горутины не могут его захватить, даже если он свободен, а становятся в конец очереди.
    • Цель: Обеспечить справедливость и предотвратить ситуацию, когда некоторые горутины никогда не получают доступ к ресурсу.
package main

import "sync"

var mu sync.Mutex

func criticalSection() {
    mu.Lock() // Захват мьютекса. Горутина может войти в ожидание.
    // ... критическая секция ...
    mu.Unlock() // Освобождение мьютекса для других горутин.
}

Эта двухрежимная система позволяет sync.Mutex быть одновременно высокопроизводительным при низкой конкуренции и справедливым при высокой.

Ответ 18+ 🔞

Да ты посмотри, какой этот sync.Mutex хитрый сука! Он там внутри не просто защелку тупую дергает, он, блядь, два режима переключает, как по погоде, чтоб всех и по быстрее, и по честности.

Вот смотри, как он изворачивается:

  1. Обычный режим, когда всё спокойно (Normal mode)

    • Когда это: Ну, мьютекс свободен или народу за ним — кот наплакал.
    • Как работает: Горутина прибегает — и сразу пытается его атомарно цапнуть, на месте, по-быстрому, пару циклов покрутиться. Если повезло и предыдущий как раз отпустил — всё, она уже в деле, даже в сон не уходила, красота! Это ж нахуй быстрее, чем контекст переключать.
    • Но подвох: А если не повезло, она в очередь встает. И вот тут возможен пиздец под названием «захват на опережение» (barging). Новая, наглая горутина может влететь и перехватить мьютекс прямо из-под носа у той, что уже сто лет в очереди торчит. Несправедливо, блядь!
  2. Режим «всех по очереди, а то сожрут» (Starvation mode)

    • Когда включается: А вот когда какая-нибудь бедолага в очереди дольше 1 миллисекунды проторчала. Мьютекс смотрит на это и думает: «Ну ёпта, тут уже не до шуток, включаем справедливость».
    • Как работает: Теперь — строгая очередь, блядь! Кто дольше ждал — тот первый и получает. Новоприбывшие, даже если мьютекс в этот миг свободен, не имеют права — марш в конец очереди, ждать своего часа. Никаких наглых перехватов.
    • Смысл: Чтобы ни одна горутина не чувствовала себя, как последняя манда, которую все игнорируют. Чтобы доступ к ресурсу получили все, а не только самые шустрые.
package main

import "sync"

var mu sync.Mutex

func criticalSection() {
    mu.Lock() // Пытаемся захватить. Тут либо быстро, либо в сон, либо в очередь — как мьютекс решит.
    // ... делаем тут своё грязное дело, в критической секции ...
    mu.Unlock() // Отпускаем. Теперь он, хитрая жопа, решит, кому его отдать: следующему в очереди или какому-нибудь новому наглецу.

Вот такая, блядь, система! Полупидор, полугений. Для простых случаев — быстрая как хуй с горы, а когда начинается давка — включает режим «всех по пайке», чтобы не было драк и обид. Умно, ёпта. Сам от такого дизайна охуел, когда впервые прочитал.