최근 면접에서 Go의 Context가 뭐냐고 물으셨다.
가장 중요할 때 기억이 나지 않는 게 세상의 규칙인 건가.. 라는 생각이 들었다
그렇게 백수의 기간이 늘었다
Context 정의
context 패키지에서 제공하는 걸로 간단하게 생각하면 작업 명세서라 할 수 있다.
"10시부터 12시는 네가 일해, 그리고 중간에 냉장고 정리를 하고 에어컨을 꺼야 돼, 나는 여기서 청소하고 있을게"와 어떻게 하지를 정의해주는 작업 명세서다.
새로운 고루틴을 시작할 때 시간을 지정해주기도 하고 외부의 작업을 취소할 때도 사용할 수 있다.
거기에 작업 설정에 대한 정보도 전달이 가능하다.
이게 가능해진 버전은 Go 1.7 버전(2016-08-15일)부터로 그때부터 기본으로 탑재가 됐다.
Context 단어 뜻을 그대로 직역하면 "맥락"으로 대충 맥락을 책임진다 생각해도 될 것 같다.
https://pkg.go.dev/context#pkg-index
문서에는 아래와 같은 다양한 함수와 타입을 제공한다고 알려주는데
Context가 파라미터로 있고 반환에도 Context가 있는 경우는 상위 Context를 Context로 감싸 신규 Context로 만들 수 있다는 걸 알려준다.
함수 및 타입들
당연히 문서를 보는 게 좋지만 진짜 간단히 정리하자면 아래와 같다
명칭 | 설명 |
func AfterFunc | Context가 완료 또는 종료, 취소시 설정한 함수가 실행, 이미 완료시는 바로 함수 실행 |
func Cause | Context가 취소된 원인을 0이 아닌 오류 반환 |
func WithCancel | 해당 Context에 CancelFunc를 받을 수 있음 |
func WithCancelCause | CancelFunc 대신 CancelCauseFunc으로 Context Cause 설정, Cause로 탐색 가능 |
func WithDeadline | 제한 기간을 지정하는데 제한 기간뿐만아니라 상위 Context가 종료시에도 종료 됨 |
func WithDeadlineCause | 반환된 CancleFunc은 Cause를 기록하지 않고 기한 초과시에 Context Cause 설정 |
func WithTimeout | 얼마나 유지할 지를 결정할 때 사용하며 Conext 취소시 관련 리소스를 해제하므로 Defer를 통한 취소가 필요 |
func WithTimeoutCause | 시간 만료시 Context Cause를 설정 |
type CacnelFuc | 작업 중단을 지시 |
type CancelCauseFunc | CancelFunc와 동일하지만 Cause 설정 |
정독하면은 Context에 대한 느낌이 올 것이다.
하지만 이걸 고루틴하고 어떻게 짬뽕시킬지에 대해서는 막막하니
예제를 코딩해봅시다.
WithCancel
작업 취소가 가능한 컨텍스트로 아래가 예제입니다.
cancel을 실행함으로써 ctx.Done을 찾는 select문이 실행되어 wg.Done으로 wg.Wait가 종료되는 것이죠.
package main
import (
"context"
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
ctx, cancel := context.WithCancel(context.Background())
go printTick(ctx)
time.Sleep(5 * time.Second)
cancel() // cancel을 실행함으로써 ctx에 done 신호가 들어감
wg.Wait()
}
func printTick(ctx context.Context) {
// 초당 한번씩 이벤트 생성
tick := time.Tick(1 * time.Second)
for {
select {
case <-ctx.Done(): // ctx에 done 신호가 들어오면
fmt.Println("cancel() 실행으로 ctx.Done()이 실행됨")
wg.Done() // waitGroup을 종료
return
case <-tick:
fmt.Println("tick")
}
}
}
다음과 같은 출력이 나와야 함
tick
tick
tick
tick
tick
cancel() 실행으로 ctx.Done()이 실행됨
WithCancelCause
위에 WithCancel 코드에서 main 부분만 아래로 바꾸면 되고
둘은 기능은 똑같으나 Cause 키워드가 붙은 것들은 Cause를 통해 취소나 완료된 이유를 알 수 있다.
func main() {
wg.Add(1)
ctx, cancel := context.WithCancelCause(context.Background())
go printTick(ctx)
time.Sleep(5 * time.Second)
cancel(errors.New("정상 종료")) // cancel을 실행함으로써 ctx에 done 신호가 들어감
// 해당 context 에 cause 로 에러 확인
err := context.Cause(ctx)
fmt.Println("Error:", err)
wg.Wait()
}
결과
tick
tick
tick
tick
tick
Error: 정상 종료
cancel() 실행으로 ctx.Done()이 실행됨
WithDeadline
데드라인은 특정 시기까지 유지되다가 지나면 만료되는 함수이다
cancel을 받을 경우 특정 시기에 만료되는 걸 취소할 수 있다
main 함수 로직만 아래로 수정하면 된다
func main() {
wg.Add(1)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
go printTick(ctx)
defer cancel()
wg.Wait()
}
결과
tick
tick
tick
tick
tick
cancel() 실행으로 ctx.Done()이 실행됨
WithTimeOut
WithTimeOut은 특정 시간 후에 종료할 수 있고
예제는 WithDeadline 부분을 아래로 바꾸면 바로 적용 가능
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
결과 값도 동일
WithValue
Context 설명 중 작업 지시 뿐만 아니라 값의 전달도 있었는데
값의 전달은 WithValue를 사용하면 된다
package main
import (
"context"
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
// 컨텍스트 랩핑
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
ctx = context.WithValue(ctx, "hello", "world")
go printTick(ctx)
defer cancel()
wg.Wait()
}
func printTick(ctx context.Context) {
// 초당 한번씩 이벤트 생성
tick := time.Tick(1 * time.Second)
for {
select {
case <-ctx.Done(): // ctx에 done 신호가 들어오면
fmt.Println("cancel() 실행으로 ctx.Done()이 실행됨")
wg.Done() // waitGroup을 종료
return
case <-tick:
fmt.Println(ctx.Value("hello"), "tick")
}
}
}
'Go Lang > Study' 카테고리의 다른 글
[GoLang] Go에서 동시성이란 (1) | 2024.01.15 |
---|---|
[GoLang] 데드락, 라이브락, 기아상태 이해하기 (Deadlock, Livelock, Starvation) (1) | 2024.01.02 |
[GoLang] Markdown을 HTML로 변환하기 (고도화) (0) | 2023.11.12 |
[GoLang] Markdown을 HTML로 변환하기 (0) | 2023.11.05 |
[GoLang] Interface와 덕 타이핑 (0) | 2023.11.05 |