Comments 6
И вам не болейте.
Я так понял это при повышенной температуре такое в голову пришло....
Я вот большей половины таких, за уши притянутых примеров кривых деферов не видел и представить себе не мог, в каком смутном состоянии разума можно было бы такие ногострелы написать. Теперь понимаю. Понимаю, что с температурой код лучше не писать.
Возможно, я что-то не так понял, но починкой я бы это не назвал: да, избавились от дедлока, но вывели из под защиты e.proxy(). Если бы этот метод был безопасен сам по себе, то и мьютексом в Global() его закрывать не было бы смысла. А если небезопасен, то теперь он стал открыт.
func (e *Example) Global() error {
func() {
e.m.Lock()
defer e.m.Unlock()
}()
return e.proxy()
}
Это равнозначно просто стиранию defer. Множество потоков могут получить и тут же вернуть блокировку, а затем дружно толпой пойти в небезопасный e.proxy().
func (e *Example) Global() error {
e.m.Lock()
e.m.Unlock()
return e.proxy()
}
Мы заключим работу мьютекса в анонимную функцию, что б defer отработал внутри нее и global пошла дальше, с открытым мьютекcом
Зачем это может быть нужно? Зачем тогда мьютекс? Привычная логика мьютекса здесь, - как можно предполжить, - в том, чтобы весь код, вызываемый упомянутой функцией, работал в эксклюзивном режиме. То, что вызывается силами конструкции return my_func() - это тоже код, работающий в логическом контексте вызывающей функции (ну, перепишем на res := my_func() и т.д.). Ведь defer же не просто так отрабатывает "после return" - такая последовательность выбрана именно для того, чтобы предоставить языковой механизм для отложенного выполнения конструкций, "закрывающих" сразу всё дерево данной функции. Не нужно считать это за недостаток - Go не обязывает использовать defer для всего и сразу.
Более того, в работе вы можете и не заметить нюанса, а именно, defer отрабатывает ПОСЛЕ отработки return, но до непосредственного схлопывания стека
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界", test())
}
func test() (n int) {
defer func() {
n++
}()
return 0
}
к
https://go.dev/play/p/3dvqxPuB4F6
Выводит 1.
Это немного другой прикол с defer - изменение возвращаемых значений:
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界", test())
}
func test() (n int) {
defer func() {
fmt.Println(n)
n = 2
}()
return 1
}
Да. Похоже это разница между выводом через return локальной переменной и именованным выводом без return. Так что автор тоже прав.
Вы уверены, что defer всегда безопасен?