Отсю заявление является очень полезной функцией в ходе, что может быть выполнено, когда способ возвращается к способу метода метода. В практических применениях отбор отбор может действовать как роль попробовать ... поймать ... на других языках или обрабатывать закрыть ручку файла и других отделочных операций.
отложить время запуска
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
Время выполнения defer объясняется в официальной документации Go соответственно.
- Когда функция, обертывающая defer, возвращается
- Когда функция переноса defer выполняется до конца
- Когда горутина, где он находится, паникует
отложить заказ на исполнение
Когда в одном методе есть множественные дефекты, отсюда задержит метод «стек» выполнен. Когда отсюда срабатывает, все «стеки» методы «OUT» и выполняют. Таким образом, порядок исполнения отсрочки - это Lifo.
Таким образом, вывод приведенного ниже кода будет не 1 2 3, а 3 2 1.
func stackingDefers() {
defer func() {
fmt.Println("1")
}()
defer func() {
fmt.Println("2")
}()
defer func() {
fmt.Println("3")
}()
}
Яма 1: Различная производительность отсрочки в анонимном возвращаемом значении и функции именованного возвращаемого значения
Давайте сначала посмотрим на результаты следующих двух методов.
func returnValues() int {
var result int
defer func() {
result++
fmt.Println("defer")
}()
return result
}
func namedReturnValues() (result int) {
defer func() {
result++
fmt.Println("defer")
}()
return result
}
Приведенный выше метод выводит 0, следующий метод выводит 1. Приведенный выше метод использует анонимные возвращаемые значения, а следующий использует именованные возвращаемые значения.В остальном логика такая же.Почему выходные результаты отличаются?
Чтобы понять эту проблему, вам сначала нужно понять логику выполнения defer.В документе говорится, что оператор defer запускается «когда» метод возвращается, а это означает, что return и defer выполняются «одновременно». Возьмем в качестве примера метод анонимного возвращаемого значения. Процесс выглядит следующим образом.
- Присвоить результат возвращаемому значению (можно понять, что Go автоматически создает возвращаемое значение retValue, что эквивалентно выполнению retValue = результат)
- Затем проверьте, есть ли отсрочка, если есть, выполните
- Возвращает только что созданное возвращаемое значение (retValue)
В этом случае изменение в defer выполняется с результатом, а не с retValue, поэтому defer по-прежнему возвращает retValue. В именованном методе возвращаемого значения, поскольку возвращаемое значение было определено при определении метода, нет процесса создания retValue, результатом является retValue, и модификация результата с помощью отсрочки также будет возвращена напрямую.
Яма 2: проблемы с производительностью, которые могут быть вызваны использованием отсрочки в цикле for
см. код ниже
func deferInLoops() {
for i := 0; i < 100; i++ {
f, _ := os.Open("/etc/hosts")
defer f.Close()
}
}
Отсрочка работает рядом с оператором, который создает ресурс, и, похоже, с логикой проблем нет. Однако, по сравнению с прямым вызовом, выполнение defer имеет дополнительные накладные расходы, например, defer скопирует в память нужные позже параметры, а также потребует push и pop структуры defer. Следовательно, определение отсрочки в цикле может вызвать большие накладные расходы.В этом примере отсрочка перед оператором f.Close() может быть удалена, чтобы уменьшить дополнительное потребление ресурсов, вызванное большим количеством отложений.
Яма 3: Убедившись, что в выполнении нет ошибки, отложите выпуск ресурсов
Некоторые операции для получения ресурсов могут вернуть параметр ERR, мы можем выбрать игнорировать возвращенный параметр err, но если вы хотите использовать отложить для отложенного выпуска, вам нужно определить, есть ли ошибаться перед использованием отсрочки, если ресурс не получен Успешно, то есть нет, нет необходимости или необходимости выполнять операцию выпуска на ресурсе снова. Если операция выпуска выполняется без судейства, является ли приобретение ресурсов успешным, он также может вызвать ошибку в выполнении метода выпуска.
Правильное написание выглядит следующим образом.
resp, err := http.Get(url)
// 先判断操作是否成功
if err != nil {
return err
}
// 如果操作成功,再进行Close操作
defer resp.Body.Close()
Яма 4: отсрочка не будет выполняться при вызове os.Exit
Когда происходит паника, все отсрочки в горутине будут выполнены, но когда для выхода из программы вызывается метод os.Exit(), отсрочки выполняться не будут.
func deferExit() {
defer func() {
fmt.Println("defer")
}()
os.Exit(0)
}
Вышеуказанная отсрочка не выводится.