golang 기초 - 컨텍스트 종류

한나리·2025년 1월 17일

Go

목록 보기
16/19
post-thumbnail

컨텍스트 종류

‼️ 들어가기전에

  • 채널은 고루틴 간 메시지를 전달하는 메시지 큐이다.
  • 채널을 이용해서 뮤텍스 없이 동시성 프로그래밍을 할 수 있다.
  • 생산자 소비자 패턴은 동시성 프로그램에서 많이 사용되고, 채널을 이용해서 구현할 수 있다.
  • 컨텍스트는 작업자에게 일을 지시할 때 사용하는 작업 명세서이다.
  • 컨텍스트를 활용해서 특정 시간 동안 작업을 지시하거나 외부에서 취소할 수 있다.
  • 채널을 제때 닫아주지 않으면 무한히 대기하는 좀비 루틴들이 생성되어 프로그램 성능이 저하되고 메모리 사용이 계속 증가되는 문제가 발생할 수 있다.

GO에서 context는 다양한 종류의 컨텍스트를 제공. 모든 컨텍스트는 context.Context 인터페이스를 구현하며, 이를 기반으로 다양한 유형의 컨텍스트를 생성할 수 있습니다. 각 종류는 특정 목적에 맞게 설계되었습니다.

Overview

  • context.Background
  • context.TODO
  • context.WithCancel
  • context.WithTimeout
  • context.WithDeadline
  • context.WithValue
컨텍스트 종류취소 가능타임아웃 / 데드라인데이터 저장 가능사용사례
context.BackgroundXXX루트 컨텍스트
context.TODOXXX임시 또는 미완성 작업
context.WithCancelOXX고루틴 명시적 취소
context.WithTimeoutOOX시간 제한 작업
context.WithDeadlineOOX특정 시점까지의 작업
context.WithValueXXO요청 범위 데이터 전달

context.Background

✔️ 설명

  • 최상위 컨텍스트로, 부모가 없는 루트 컨텍스트
  • 애플리케이션의 메인 함수, 초기화, 테스트 코드에서 주로 사용
  • 일반적으로 다른 컨텍스트를 생성할 때 기반으로 사용

✔️ 특징

  • 취소나 타임아웃이 없음
  • 상태나 값이 포함되지 않음

✔️ 예시

ctx := context.Background()

context.TODO

✔️ 설명

  • 아직 결정되지 않은 작업이나 구현이 완료되지 않은 코드에서 임시로 사용
  • context.Background와 유사하지만, 의도적으로 "TODO상태"임을 나타내는 용도

✔️ 특징

  • context.Backgound와 동일한 기능
  • 코드의 미완성 상태를 나타내기 위한 명시적 표현

✔️ 예시

ctx := context.TODO()

context.WithCancel

✔️ 설명

  • 취소 가능 컨텍스트를 생성
  • 부모 컨텍스트를 기반으로, cancel() 호출 시 취소 신호가 전달

✔️ 특징

  • 자식 고루틴을 명시적으로 종료할 수 있음
  • 부모 컨텍스트가 취소되면, 자식 컨텍스트도 자동으로 취소됨

✔️ 예시

ctx := cancel := context.WithCancel(context.Background())
go func() {
	<-ctx.Done() // 취소 신호 대기
    fmt.Println("Context canceled")
}()
cancel() //취소

context.WithTimeout

✔️ 설명

  • 특정 시간 이후에 자동으로 취소되는 컨텍스트를 생성
  • 부모 컨텍스트를 기반으로, 지정된 시간이 지나면 취소

✔️ 특징

  • 타임아웃 기반으로 고루틴을 관리
  • 타임아웃이 발생하면 ctx.Err()에서 context.DeadlineExceeded를 반환

✔️ 예시

ctx, cancel := context.WithTimeout(context.Backgound(), 2*time.Second)
defer cancel()

select {
case <- time.After(3*time.Second):
	fmt.Println("Operation completed")
case <- ctx.Done():
	fmt.Println("Context timed out:", ctx.Err()) //타임아웃 발생
}

context.WithDeadline

✔️ 설명

  • 특정 시점까지 유효한 컨텍스트를 생성
  • withTimeout과 유사하지만, 절대적인 데드라인(시점)을 지정

✔️ 특징

  • 타임아웃 대신 명시적인 데드라인 설정
  • 타임아웃처럼 ctx.Err()에서 context.DeadlineExceeded를 반환

✔️ 예시

deadline := time.Now().Add(5*time.Second)
ctx, cancel := context.WithDeadlin(context.Backgound(), deadline)

defer cancel()

select {
case <- time.After(10*time.Second):
	fmt.Println("Operation completed")
case <- ctx.Done():
	fmt.Println("Context reached deadline:", ctx.Err()) //데드라인 도달
}

context.WithValue

✔️ 설명

  • 컨텍스트에 키-값 데이터를 저장할 수 있는 컨텍스트를 생성
  • 주로 요청 범위의 데이터를 저장하는 데 사용

✔️ 특징

  • 컨텍스트는 읽기 전용이며, 데이터를 수정할 수 없음
  • 저장된 데이터는 interface{} 타입으로 저장되므로 적절한 타입 단언이 필요
  • 데이터 접근 시 부모 컨텍스트를 따라 올라가면서 검색

✔️ 예시

ctx := context.WithValue(context.Background(), "number", "value")
value := ctx.Value("number")
fmt.Println("value:", value.(int)

컨텍스트 간의 관계

컨텍스트는 트리 구조를 형성

  • 부모-자식 관계 :
    - 새로운 컨텍스트를 생성하면 부모 컨텍스트를 기반으로 하위 컨텍스트가 만들어짐
    • 부모가 취소되면 자식 컨텍스트도 자동 취소

컨텍스트 사용 사례

  1. HTTP 요청 관리
    • 요청 처리 중 타임아웃이나 취소가 필요한 경우 사용
    • 예: HTTP 서버에서 클라이언트 요청 취소 처리
  2. 고루틴 관리
    • 여러 고루틴 간의 작업을 동기화하거나 취소 신호 전달
  3. 데이터 전달
    • 트랜잭션 ID, 사용자 인증 정보, 로깅 데이터를 요청 범위에서 전달

고루틴, 채널, 컨텍스트의 관계성

GO의 고루틴, 채널, 컨텍스트는 동시성 프로그래밍에서 서로 밀접하게 연결
이 세 가지 개념은 동시 작업의 관리, 취소 신호 전달 그리고 데이터 통신을 조율하는 데 사용

고루틴

고루틴은 GO의 경량 스레드로, 동시성을 구현하는 기본 단위.

✔️ 컨텍스트 와의 관계
1. 컨텍스트에 의한 제어
- 고루틴은 context.Done() 채널을 통해 컨텍스트의 취소 신호를 감지하고 작업을 종료할 수 있음
- 여러 고루틴이 같은 컨텍스트를 공유하여 중앙에서 제어
2. 컨텍스트를 통한 부모-자식 관계
- 컨텍스트는 트리 구조로 구성되므로, 부모 고루틴에서 생성된 컨텍스트는 자식 고루틴에도 영향을 미침
- 부모 고루틴이 취소되면 자식 고루틴도 취소
3. 자원 정리
- 컨텍스트를 통해 고루틴을 정리하지 않으면 메모리 누수가 발생할 수 있음

채널

채널은 고루틴 간의 데이터 교환 및 동기화를 위한 도구.

✔️ 컨텍스트 와의 관계
1. 컨텍스트로 채널 종료 제어
- context.Done() 채널을 감시하여 데이터 송수신 작업을 중단할 수 있음
- 예: 작업이 취소되면 채널에서 더이상 데이터를 보내지 않도록 설정
2. 컨텍스트로 채널 대체
- 컨텍스트는 context.Done() 채널을 통해 취소 신호를 전파하므로, 경우에 따라 데이터 전송보다는 제어 신호 전달용으로 사용될 수 있음
3. 고루틴 간 작업 분배
- 채널을 통해 고루틴 간 작업을 분배하면서, 컨텍스트로 작업 종료 신호를 함께 관리할 수 있음

📌 고루틴, 채널, 컨텍스트의 상관관계

  1. 취소 신호 전달
    • 컨텍스트는 고루틴 간에 작업 취소 신호를 전달하기 위한 도구로 사용
    • context.WithCancel이나 context.WithTimeout을 동해 Done() 채널을 전달하고, 고루틴은 이를 감지하여 종료 작업을 수행
  2. 데이터 전달 및 동기화
    • 채널은 고루틴 간의 데이터 교환에 사용
    • 컨텍스트를 통해 작업이 취소되면, 채널 송수신을 중단하고 고루틴을 종료
  3. 동시 작업의 부모-자식 구조
    • 컨텍스트는 고루틴 간의 부모-자식 관계를 형성
    • 부모 고루틴의 컨텍스트가 취소되면, 자식 고루틴도 취소
  4. 타임아웃/데드라인 관리
    • 컨텍스트는 고루틴의 실행 시간과 자원을 제한
    • 타임아웃 발생 시 관련 채널 작업도 중단

예시 코드

1. 컨텍스트와 고루틴의 상관관계를 보여주는 코드 예제

package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context, id int) {
	for {
		select {
		case <-ctx.Done(): // 컨텍스트 취소 신호 감지
			fmt.Printf("Worker %d: stopping\n", id)
			return
		default:
			fmt.Printf("Worker %d: working\n", id)
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())

	// 2개의 고루틴 실행
	go worker(ctx, 1)
	go worker(ctx, 2)

	time.Sleep(2 * time.Second) //2초 대기 후 cancel()호출로 ctx.Done() 채널이 닫힘. 두 고루틴이 ctx.Done()을 감지하고 따라서 종료.
	fmt.Println("Main: canceling context")
	cancel() // 컨텍스트 취소

	time.Sleep(1 * time.Second) //1초 대기 후 프로그램 종료
	fmt.Println("Main: finished")
}
//결과
Worker 1: working
Worker 2: working
...
Main: canceling context
Worker 1: stopping
Worker 2: stopping
Main: finished

context.WithCancel로 새로운 컨텍스트 ctx와 취소함수 cancel을 생성하고 ctx는 context.Background를 기반으로 하며, cancel() 호출 시 취소 신호가 전달

2. 컨텍스트, 고루틴, 채널 통합 예제

package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context, ch chan int) {
	for {
		select {
		case <-ctx.Done(): //ctx.Done()과 채널을 감시. ctx.Done()이 닫히면 작업 중단
			fmt.Println("Worker: context canceled, stopping")
			return
		case n := <-ch:
			fmt.Printf("Worker: received %d\n", n)
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) //5초 타임아웃 컨텍스트 생성. 5초가 지나면 ctx.Done()채널이 닫혀 취소 신호가 전파
	defer cancel()

	ch := make(chan int)

	go worker(ctx, ch) //worker 고루틴이 실행되어 채널 ch와 ctx.Done()을 감시

	for i := 0; i < 10; i++ {
		select {
		case <-ctx.Done(): //ctx.Done()감시하여 타임아웃 발생 시 작업 중단
			fmt.Println("Main: context canceled, exiting")
			return
		case ch <- i: //데이터를 채널에 전송
			fmt.Printf("Main: sent %d\n", i)
			time.Sleep(1 * time.Second)
		}
	}
}
//결과
Main: sent 0
Worker: received 0
Main: sent 1
Worker: received 1
Main: sent 2
Worker: received 2
Main: context canceled, exiting
Worker: context canceled, stopping
profile
내가 떠나기 전까지는 망하지 마라, 블록체인 개발자

0개의 댓글