Search
Write a publication
Pull to refresh

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
}

https://go.dev/play/p/oCpFl6trX_6

Да. Похоже это разница между выводом через return локальной переменной и именованным выводом без return. Так что автор тоже прав.

Sign up to leave a comment.