질문 배경:
처음부터 Go 언어는 시간 관리를 위한 기본 함수인 time.Sleep과 time.After를 포함하는 time 패키지를 제공했습니다. 시스템 수면 (System.sleep)과는 달리, Go는 비동기 타이머를 고유한 원시 타입으로 구현하여 멀티 스레드 작업에 적합하도록 했습니다.
문제:
개발자들은 자주 time.Sleep을 작업 사이의 일시 정지 구현에 잘못 사용합니다. 이는 동시성 프로그램에서 바람직하지 않습니다. Go 패러다임에서는 사건 기다림을 채널과 time.After를 통해 select/채널과 통합하여 관리하는 것이 올바릅니다.
해결책:
time.Sleep(d)는 현재 고루틴을 d 시간 동안 차단하며, 이는 직접적인 "수면"입니다. time.After(d)는 d 후에 이벤트 시간이 발생할 채널을 반환합니다. 후자는 select에서 채널을 더 유연하게 활용할 수 있고, 중단 가능한 대기 및 타임아웃에 편리합니다.
코드 예:
ch := make(chan struct{}) go func() { time.Sleep(2 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("done") case <-time.After(1 * time.Second): fmt.Println("timeout") }
주요 특징:
time.Sleep을 사용하여 프로그램 전체의 실행을 차단할 수 있습니까?
아니요, time.Sleep은 단지 하나의 고루틴을 블록화하며, 나머지는 계속 실행됩니다.
time.After가 채널을 읽지 않으면 메모리 누수를 일으킬 수 있습니까?
네, 값이 읽힐 때까지 타이머가 보존되므로 읽지 않으면 가비지 컬렉터가 객체를 제거하지 않습니다.
코드 예:
func leak() { for { _ = time.After(time.Hour) } }
이벤트를 받지 못할 때 time.After의 대기를 올바르게 취소하는 방법은 무엇입니까?
타이머를 사용하여 수동으로 멈추는 것이 좋습니다. 만약 대기를 조기 종료하려면:
t := time.NewTimer(time.Minute) if done { t.Stop() }
고루틴이 작업으로부터 신호를 기다리고, 타임아웃의 경우 다음과 같이 설정합니다:
time.Sleep(10*time.Second) doSomething()
장점:
단점:
코드는 select와 time.After를 통해 구조화됩니다:
select { case <-workSignal: // 실행 case <-time.After(10 * time.Second): // 타임아웃 시 수행 }
장점:
단점: