Pull to refresh

Comments 22

Супер. У меня всего два c половиной вопроса
1) Каналы изнутри сами построены на мьютексах?
2) Когда мне использовать мьютекс, а когда буфферизованный канал размером 1?
Тут мне кажется, что разницы между
mutex.Lock()
xxx
mutex.Unlock()

и
<- chan
xxx
1 -> chan

нет, но второй вариант безопаснее. То есть каналы не сложнее мьютекса в самом коде, но безопаснее.

2.5) рантайм Go отлично шедулит горутины когда они засыпают на ожидании данных из канала — делает ли он так же и для мьютексов?
Буферизированный канал, по сути, семафор. На счет безопасности, не вижу разницы. Разве что код с мьютексом будет куда очевиднее и скорее всего быстрее.
1. Каналы хитрые. Некоторые операции делаются с оптимистичными блокировками, некоторые чисто на мьютексах.
2. Каналы нужно использовать по-другому. Это не слепая замена мьютексам. Надо на каждый разделяемый ресурс запускать отдельную горутину, которой присылать команды на изменение данных и запросы на чтение. Эта горутина просто исполняет все, о чем её просят, гарантированно последовательно.

По сути ваших примеров кода — аналог мьютекса так можно сделать. За исключением всяких крайних случаев типа двойного вызова Unlock.
1) Каналы используют блокировки, но технически они не являются sync.Mutex блокировкой. Будет точнее сказать, что блокировки для реализации каналов, мютексов, семафоров, рлоков и т.д. используют атомарные процессорные инструкции (типа CaS).
2) Разницы нет в достигнутом результате, но семантически первый вариант корректнее. Откуда мне знать, что у Вас используется именно буферизированный канал размером 1? А когда я вижу mutex.Lock() — то я знаю что это ексклюзивная блокировка.
2.5) «отлично» это слишком сильно сказано. Неплохо — это факт. Но накладные расходы на шедулинг каналов все ещё не дают признать их самым предпочтительным средством разделения доступа. А мютексы рантайм тоже шедулит, если встречает лок.
Если я правильно понимаю, то при работе с каналами невозможно обнаружить взаимоблокировку автоматически, в отличии от мьютексов.

В этом плане мьютексы безопаснее каналов.
Спасибо, я почему-то как раз считал именно наоборот, поэтому и думал, что каналы безопаснее. Спасибо. Это ключевой момент, как мне кажется, должен быть упомянут в статье.
defer mutex.unlock — это, конечно, хорошо, только… Я правильно понимаю, что «правильный» lock_guard в Go сделать не получится?
defer unlock даёт те же гарантии, что и lock_guard — при выходе из функции (любом, даже через panic) мьютекс освободится. Я бы сказал, что defer — это оригинальная идея, которая позволяет избежать использования guard'ов и предоставить аналогичную функциональность вообще для любых объектов. Открыли соединение — defer закрыть. Получили блокировку — defer отпустить. Для случаев с более сложной логикой лучше не танцевать с мьютексами, см. статью.
Те же, да не совсем.

lock_guard — это такой способ блокирования мьютекса, что даже самый криворукий программист не сможет забыть его разблокировать.

А забыть написать `defer mutex.Unlock()` можно запросто.
Так же можно забыть и safe_guard создать. Учитывая, что defer unlock пишется на следующей строчке после lock, забыть его ну очень сложно.
Нельзя, если это единственный интерфейс работы с объектом, защищённый мьютексом.

И вообще, в случае mutex guard за эту часть дизайна отвечает разработчик языка/стандартной библиотеки, а не конечный разработчик.
Кто ж блокировками заставляет пользователя заниматься? Пользователю снаружи виден метод, а его реализация уже делает все необходимые блокировки. И если она это делает, то делает lock() и defer unlock(). Сразу. Рядом. Без шансов забыть unlock. Об этом речь идёт.
Кто ж кто ж, автор статьи пользуется же.

Он пользователь, и для него интерфейс мьютекса — такой. Возможность забыть разблокировку есть.
Вообще говоря, lock_guard дает гарантию блокировки мьютекса при создании на стеке и автоматической разблокировки при выходе из области видимости (а не из функции). При любом выходе, вызывающем раскрутку стека.
Это другой вопрос. Я писал лишь о том, что defer и lock_guard — это не вполне эквивалентные механизмы.
Можно и mutex.Lock() забыть написать. И вообще забыть на работу пойти.
Особенно в пятницу.
что хорошего в этом го? чем он лучше других ЯП?
где нормальные инструменты под него? а не только подсветка синтаксиса.

err :=…
if err != nil {
//log error
return // < — разблокировка произойдет здесь
}

так вроде не пишут, не?

if err := ..; err != nil {

вместо решения проблем, похоже некоторым нравится их создавать
Что хорошего в этом комментарии? Чем он полезен для посетителей?
Где нормальное аргументирование своих слов? А не только «разнос» без всякого разбора в ситуации.
> Обратите внимание в этом коде, что для каждого не-экспортированного метода есть аналогичный экспортированный. Эти методы работают как публичный API, и заботятся о блокировках на этом уровне. Далее они вызывают неэкспортированные методы, которые вообще не заботятся о блокировках. Это гарантирует, что все вызовы ваших методов извне будут блокироваться лишь раз и лишены проблемы рекурсивной блокировки.

Копипаста и никем не проверяемые конвенции. Славно. В том же D достаточно объявить класс как synchronized и все публичные методы автоматом будут завёрнуты в локи, а прямой доступ к полям вообще запрещён извне.
Вам никто не запрещает писать на D. Но это хаб о Go.
Знаете, мне этот го напоминает прыщавого подростка — весь такой из себя уверенный в своей исключительности, знает как надо, учиться у взрослых ничему не хочет, зато понтуется сколько он намайнкрафтил словно великим достижением :-D
Sign up to leave a comment.

Articles