‼️ 들어가기전에
- 채널은 고루틴 간 메시지를 전달하는 메시지 큐이다.
- 채널을 이용해서 뮤텍스 없이 동시성 프로그래밍을 할 수 있다.
- 생산자 소비자 패턴은 동시성 프로그램에서 많이 사용되고, 채널을 이용해서 구현할 수 있다.
- 컨텍스트는 작업자에게 일을 지시할 때 사용하는 작업 명세서이다.
- 컨텍스트를 활용해서 특정 시간 동안 작업을 지시하거나 외부에서 취소할 수 있다.
- 채널을 제때 닫아주지 않으면 무한히 대기하는 좀비 루틴들이 생성되어 프로그램 성능이 저하되고 메모리 사용이 계속 증가되는 문제가 발생할 수 있다.
GO에서 context는 다양한 종류의 컨텍스트를 제공. 모든 컨텍스트는 context.Context 인터페이스를 구현하며, 이를 기반으로 다양한 유형의 컨텍스트를 생성할 수 있습니다. 각 종류는 특정 목적에 맞게 설계되었습니다.
| 컨텍스트 종류 | 취소 가능 | 타임아웃 / 데드라인 | 데이터 저장 가능 | 사용사례 |
|---|---|---|---|---|
| context.Background | X | X | X | 루트 컨텍스트 |
| context.TODO | X | X | X | 임시 또는 미완성 작업 |
| context.WithCancel | O | X | X | 고루틴 명시적 취소 |
| context.WithTimeout | O | O | X | 시간 제한 작업 |
| context.WithDeadline | O | O | X | 특정 시점까지의 작업 |
| context.WithValue | X | X | O | 요청 범위 데이터 전달 |
✔️ 설명
✔️ 특징
✔️ 예시
ctx := context.Background()
✔️ 설명
✔️ 특징
✔️ 예시
ctx := context.TODO()
✔️ 설명
✔️ 특징
✔️ 예시
ctx := cancel := context.WithCancel(context.Background())
go func() {
<-ctx.Done() // 취소 신호 대기
fmt.Println("Context canceled")
}()
cancel() //취소
✔️ 설명
✔️ 특징
✔️ 예시
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()) //타임아웃 발생
}
✔️ 설명
✔️ 특징
✔️ 예시
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()) //데드라인 도달
}
✔️ 설명
✔️ 특징
✔️ 예시
ctx := context.WithValue(context.Background(), "number", "value")
value := ctx.Value("number")
fmt.Println("value:", value.(int)
컨텍스트는 트리 구조를 형성
GO의 고루틴, 채널, 컨텍스트는 동시성 프로그래밍에서 서로 밀접하게 연결
이 세 가지 개념은 동시 작업의 관리, 취소 신호 전달 그리고 데이터 통신을 조율하는 데 사용
고루틴은 GO의 경량 스레드로, 동시성을 구현하는 기본 단위.
✔️ 컨텍스트 와의 관계
1. 컨텍스트에 의한 제어
- 고루틴은 context.Done() 채널을 통해 컨텍스트의 취소 신호를 감지하고 작업을 종료할 수 있음
- 여러 고루틴이 같은 컨텍스트를 공유하여 중앙에서 제어
2. 컨텍스트를 통한 부모-자식 관계
- 컨텍스트는 트리 구조로 구성되므로, 부모 고루틴에서 생성된 컨텍스트는 자식 고루틴에도 영향을 미침
- 부모 고루틴이 취소되면 자식 고루틴도 취소
3. 자원 정리
- 컨텍스트를 통해 고루틴을 정리하지 않으면 메모리 누수가 발생할 수 있음
채널은 고루틴 간의 데이터 교환 및 동기화를 위한 도구.
✔️ 컨텍스트 와의 관계
1. 컨텍스트로 채널 종료 제어
- context.Done() 채널을 감시하여 데이터 송수신 작업을 중단할 수 있음
- 예: 작업이 취소되면 채널에서 더이상 데이터를 보내지 않도록 설정
2. 컨텍스트로 채널 대체
- 컨텍스트는 context.Done() 채널을 통해 취소 신호를 전파하므로, 경우에 따라 데이터 전송보다는 제어 신호 전달용으로 사용될 수 있음
3. 고루틴 간 작업 분배
- 채널을 통해 고루틴 간 작업을 분배하면서, 컨텍스트로 작업 종료 신호를 함께 관리할 수 있음
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() 호출 시 취소 신호가 전달
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