본문 바로가기

Developer/Go

Golang 고루틴(goroutine) 라이프사이클 관리 - channel

 

개요 (Overview)

 

Golang 고루틴(goroutine) 라이프사이클 관리 - context.Context

개요 (Overview) Golang에서는 고루틴이라는 비동기 작업을 생성할 수 있고 쓰레드와 비교해 가볍기 때문에 동시에 수백 개를 만들어도 메모리를 많이 사용하지 않는다. 다만 특성상 비동기로 실행

torrang.tistory.com

Golang에서 실행 중인 고루틴의 흐름을 제어할 수 있는 방법은 컨텍스트(Context) 외에도 채널(Channel)을 사용하여 흐름 제어를 할 수 있다. 채널은 다른 언어에서 일반적으로 큐(Queue)와 동일한 역할을 하고 있지만 Golang에서의 채널은 큐 외에도 흐름 제어의 역할도 가능하다. 채널은 기본적으로 별도의 동기 작업 없이도 안전하기 때문에 고루틴에서도 사용 가능하다. 참고로 Golang에서 쓰레드는 런타임 내부에서만 관리되며 개발자가 쓰레드를 제어할 수 있는 방법은 없다.

 

채널 (Channel)

기본적으로 큐의 역할을 한다.

 

아래 문서는 채널을 편하게 사용하기 위한 헬퍼 함수와 관련 변수들이 정의된 패키지이며 실제 채널 자체에 대해 다루는 문서는 아니다. 그리고 운영 환경에서 import 하는 것을 권장하지 않는다. (*Go의 타입 시스템의 한계 때문이라는데 이건 추후 확인)

channels package - github.com/eapache/channels - Go Packages

 

기본적인 사용 방법은 Effective Go 문서에서 확인할 수 있다.

Effective Go - The Go Programming Language

 

Effective Go - The Go Programming Language

Documentation Effective Go Effective Go Introduction Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straigh

go.dev

 

 

채널 생성

func main() {
    // 버퍼가 없는 bool 타입의 채널을 생성
    unBufferedChan := make(chan bool)
    
    // 버퍼가 없는 string 타입의 채널을 생성
    unBufferedChan := make(chan string, 0)
    
    // 버퍼가 10만큼 있는 string 타입의 채널을 생성
    bufferedChan := make(chan string, 10)
}

채널에 데이터 처리 (넣기/가져오기)

func main() {
    // 버퍼가 없는 bool 타입의 채널을 생성
    unBufferedChan := make(chan bool)
    
    // 채널에 true 데이터 넣기
    unBufferedChan <- true
    
    // 채널에 true 데이터 가져오기
    result := <-unBufferedChan
}

채널 닫기

func main() {
    // 버퍼가 없는 bool 타입의 채널을 생성
    unBufferedChan := make(chan bool)
    
    // 채널 닫기
    close(unBufferedChan)
}

 

사용 방법 (How to use)

함수 처리 중 특정 사유(인터럽트, 에러 등)로 인한 고루틴 흐름 제어 (채널 닫기)

  1. 타입에 관계 없이 채널 생성 (단순하게 bool 타입)
  2. 생성된 채널을 포함하여 고루틴 생성
  3. 작업 (메인 함수는 고루틴 완료 대기)
  4. 인터럽트 또는 메인 함수에서 에러가 발생하여 채널 닫기(close)
  5. 채널이 닫힌 후 실행 중인 고루틴이 모두 종료될 때까지 대기
  6. 모두 종료 후 메인 함수 정리 후 종료

 

샘플 코드 (Sample code)

이전 글과 동일하게 고루틴의 상태를 확인하기 위해 sync.WaitGroup을 사용

 

 

meaningful-go/channel-goroutine-manage at main · torrang/meaningful-go

Go언어 사용 시 유용한 코드나 패턴을 작성하여 사용하기 위한 프로젝트. Contribute to torrang/meaningful-go development by creating an account on GitHub.

github.com

package main

import (
	"log"
	"sync"
	"time"
)

// user defined asynchronous task
func async_task(wg *sync.WaitGroup, stopEvent <-chan bool, i int) {
	for {
		select {
		case <-stopEvent:
			// using break does not escape for-loop
			// should use return instead or flag
			log.Printf("terminate async task %d", i)
			wg.Done()
			return
		default:
			log.Printf("async task %d is running", i)
			time.Sleep(time.Duration(i) * time.Second)
		}
	}
}

func main() {
	log.Printf("start channel-goroutine-manage")
	waitGroup := sync.WaitGroup{}
	stopEvent := make(chan bool)

	// run goroutines
	for i := 1; i <= 3; i++ {
		waitGroup.Add(1)
		go async_task(&waitGroup, stopEvent, i)
	}

	// stop goroutines after 10 seconds
	go func() {
		log.Printf("wait for 10 seconds")
		time.Sleep(time.Duration(10) * time.Second)
		log.Printf("time is over, close channel")
		close(stopEvent)
	}()

	// wait goroutines
	log.Printf("wait goroutines")
	waitGroup.Wait()

	log.Printf("all goroutines are terminated")
	log.Printf("terminate basic-goroutine-manage")
}