Какие средства синхронизации потоков существуют: mutex, semaphore, NSLock
Что проверяют
Знаете ли вы примитивы синхронизации Go и разницу между взаимным исключением и ограничением числа участников. NSLock это класс Foundation из мира Apple, в Go его нет, и сильный ответ говорит об этом прямо. Следом обычно спрашивают, когда лучше канал, а когда мьютекс.
Как отвечать
sync.Mutex: один владелец критической секции. Нулевое значение готово к работе,Lockи сразуdefer Unlock. Мьютекс не реентерабельный: повторныйLockв той же горутине даёт дедлок;sync.RWMutex: много читателей или один писатель. Выигрывает, только когда чтений заметно больше записей;- семафор: ограничивает число одновременных участников. В Go это буферизованный канал
make(chan struct{}, n)илиsemaphore.Weightedизgolang.org/x/syncсAcquire(ctx, n), который уважает отмену контекста. Для пула задач удобенerrgroup.GroupсSetLimit(n); - NSLock: мьютекс из Foundation с методами
lock(),unlock()иtry(). Отпускать его нужно из того же потока, для рекурсивного захвата естьNSRecursiveLock. В Go ближайший аналогsync.Mutex.
Дополните остальным набором: sync.WaitGroup (с Go 1.25 у него есть метод Go), sync.Once и sync.OnceValue, типы вроде atomic.Int64 из sync/atomic, sync.Cond. И назовите принцип: каналы для передачи владения данными и координации, мьютекс для защиты общего состояния внутри структуры. Проверка в любом случае одна: тесты с флагом -race.
Где спотыкаются
Копируют структуру с мьютексом: метод с получателем по значению работает с копией замка, и защита пропадает. go vet ловит это проверкой copylocks.
Выходят из функции раньше Unlock при ошибке или панике. Спасает defer mu.Unlock() сразу после Lock.
Рекурсивно берут RLock. Если между двумя захватами пришёл писатель, новый читатель ждёт писателя, писатель ждёт первого читателя, и горутина висит.