Go를 사용 중 문득 든 궁금증이 defer 함수를 여러 개 사용한다면 실행 순서가 어떻게 되는지 궁금했다.
기본적으로는 호출한 순서대로 실행될 것 같았고 고루틴의 경우는 예외적으로 랜덤하게 실행될 것으로 생각되어 실험을 해보았다.
테스트
기본 사용 시
package main
import (
"log"
"time"
)
// 3초 후 숫자 프린트
// 결과: 3, 2, 1
func printNo() {
defer log.Println("printNo=1")
defer log.Println("printNo=2")
defer log.Println("printNo=3")
log.Println("run defer functions after 3 seconds")
time.Sleep(time.Duration(3) * time.Second)
}
// 3초 후 숫자 프린트
// 결과: 2, 3, 1
func printNo2() {
defer log.Println("printNo=1")
defer log.Println("printNo=3")
defer log.Println("printNo=2")
log.Println("run defer functions after 3 seconds")
time.Sleep(time.Duration(3) * time.Second)
}
func main() {
log.Println("run defer order")
// print number test
printNo()
printNo2()
}
결과를 보았을 때 "마지막으로 호출한 순서"대로 실행되는 것을 확인하였다.
따라서 기본 동작은 호출한 순서의 역순으로 실행된다고 볼 수 있다.
defer 내부에 고루틴 호출 시
당연한 결과겠지만 defer 내부에 고루틴을 여러 개 호출하는 경우, 랜덤하게 실행되는 것을 확인할 수 있었다.
여기서 주의할 점은 Go 1.21 이하 버전에서는 for-loop 문에서 클로저(Closure)를 사용할 경우, 클로저에서 받는 변수의 값이 고정된 값으로 받는 현상이 있다. 이는 Go에서 고루틴이 실행되기 전에 for-loop 문이 모두 실행되어 발생하는 현상으로 Go 1.22 이상에서 수정될 예정이라고 한다. 이 부분에 대해서는 나중에 따로 다뤄보려고 한다. (reference: Fixing For Loops in Go 1.22 - The Go Programming Language, Frequently Asked Questions (FAQ) - The Go Programming Language)
package main
import (
"log"
"time"
)
// 3초 후 숫자 프린트
// 결과: 3, 2, 1
func printNo() {
defer log.Println("printNo=1")
defer log.Println("printNo=2")
defer log.Println("printNo=3")
log.Println("run defer functions after 3 seconds")
time.Sleep(time.Duration(3) * time.Second)
}
// 3초 후 숫자 프린트
// 결과: 2, 3, 1
func printNo2() {
defer log.Println("printNo=1")
defer log.Println("printNo=3")
defer log.Println("printNo=2")
log.Println("run defer functions after 3 seconds")
time.Sleep(time.Duration(3) * time.Second)
}
// 고루틴 지연 실행
// 결과: 랜덤
func goroutineDefer() {
// use i in for-loop may not properly work on closure with goroutine due to bug in golang before 1.22
// this bug will be fixed in 1.22 (ref: https://go.dev/blog/loopvar-preview, https://go.dev/doc/faq#closures_and_goroutines)
for i := 0; i <= 4; i++ {
j := i
defer func() {
go log.Printf("goroutine defer exit=%d\n", j)
}()
}
log.Println("run goroutine in defer functions after 3 seconds")
time.Sleep(time.Duration(3) * time.Second)
}
func main() {
log.Println("run defer order")
// print number test
printNo()
printNo2()
// goroutine defer
goroutineDefer()
time.Sleep(time.Duration(1) * time.Second)
}
샘플 코드
meaningful-go/defer-order at main · torrang/meaningful-go (github.com)
결론
- 여러 개의 defer 함수 호출 시 호출한 순서의 역순으로 실행된다.
- defer 함수안에 여러 고루틴이 있는 경우, 당연히 실행 순서가 보장되지 않는다.
'Developer > Go' 카테고리의 다른 글
Golang 고루틴(goroutine) 라이프사이클 관리 - channel (0) | 2024.04.24 |
---|---|
Golang 고루틴(goroutine) 라이프사이클 관리 - context.Context (0) | 2024.04.21 |
컨테이너 환경에서 GOMAXPROCS 이슈 (0) | 2024.01.17 |