Go
Перенести
Поиск…
Вступление
defer
оператор нажимает вызов функции на список. Список сохраненных вызовов выполняется после возвращения внешней функции. Отсрочка обычно используется для упрощения функций, выполняющих различные действия по очистке.
Синтаксис
- отложить someFunc (args)
- defer func () {// здесь идет код} ()
замечания
Отложить работу, введя новый стек стека (вызываемую функцию после ключевого слова defer
) в стек вызовов ниже текущей исполняемой функции. Это означает, что отсрочка гарантированно будет работать до тех пор, пока стек будет размотан (если ваша программа выйдет из строя или получит SIGKILL
, отсрочка не будет выполняться).
Основы отсрочки
Оператор defer в Go является просто вызовом функции, помеченным для последующего выполнения. Оператор Defer - это обычный вызов функции с префиксом ключевого слова defer
.
defer someFunction()
Отложенная функция выполняется, как только функция, содержащая оператор defer
возвращается. Фактический вызов отложенной функции происходит, когда закрывающая функция:
- выполняет оператор возврата
- падает с конца
- паникует
Пример:
func main() {
fmt.Println("First main statement")
defer logExit("main") // position of defer statement here does not matter
fmt.Println("Last main statement")
}
func logExit(name string) {
fmt.Printf("Function %s returned\n", name)
}
Выход:
First main statement
Last main statement
Function main returned
Если функция имеет несколько отложенных операторов, они образуют стек. Последний defer
является первым, который будет выполняться после возвращения функции закрытия, после чего последующие вызовы предшествующего defer
в порядке (ниже пример возвращает, вызывая панику):
func main() {
defer logNum(1)
fmt.Println("First main statement")
defer logNum(2)
defer logNum(3)
panic("panic occurred")
fmt.Println("Last main statement") // not printed
defer logNum(3) // not deferred since execution flow never reaches this line
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Выход:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Обратите внимание, что отложенные функции имеют свои аргументы, которые оцениваются во время выполнения defer
:
func main() {
i := 1
defer logNum(i) // deferred function call: logNum(1)
fmt.Println("First main statement")
i++
defer logNum(i) // deferred function call: logNum(2)
defer logNum(i*i) // deferred function call: logNum(4)
return // explicit return
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Выход:
First main statement
Num 4
Num 2
Num 1
Если функция имеет именованные возвращаемые значения, отложенная анонимная функция внутри этой функции может получать и обновлять возвращаемое значение даже после возвращения функции:
func main() {
fmt.Println(plusOne(1)) // 2
return
}
func plusOne(i int) (result int) { // overkill! only for demonstration
defer func() {result += 1}() // anonymous function must be called by adding ()
// i is returned as result, which is updated by deferred function above
// after execution of below return
return i
}
Наконец, оператором defer
обычно используются операции, которые часто встречаются вместе. Например:
- открыть и закрыть файл
- подключать и отключать
- блокировать и разблокировать мьютексы
- пометить группу waitgroup как выполненную (
defer wg.Done()
)
Это использование обеспечивает надлежащее освобождение системных ресурсов независимо от потока выполнения.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Отсроченные вызовы функций
Отложенные вызовы функций служат аналогичной цели для таких вещей, как, finally
блоки в таких языках, как Java: они гарантируют, что какая-то функция будет выполняться, когда внешняя функция вернется, независимо от того, произошла ли ошибка или какой ответ был применен в случаях с несколькими возвратами. Это полезно для очистки ресурсов, которые должны быть закрыты, например, сетевых подключений или указателей файлов. Ключевое слово defer
указывает вызов отложенной функции, аналогично ключевому слову go
инициирующему новый goroutine. Как вызов go
, аргументы функции вычисляются немедленно, но в отличие от go
call, отложенные функции не выполняются одновременно.
func MyFunc() {
conn := GetConnection() // Some kind of connection that must be closed.
defer conn.Close() // Will be executed when MyFunc returns, regardless of how.
// Do some things...
if someCondition {
return // conn.Close() will be called
}
// Do more things
}// Implicit return - conn.Close() will still be called
Обратите внимание на использование conn.Close()
вместо conn.Close
- вы не просто передаете функцию, вы откладываете полный вызов функции, включая ее аргументы. Множественные вызовы функций могут быть отложены в одной и той же внешней функции, и каждый из них будет выполнен один раз в обратном порядке. Вы также можете отложить закрытие - просто не забывайте о парне!
defer func(){
// Do some cleanup
}()