28.1. 프로그램언어 고(Go)의 고루틴(Goroutines) 활용법

프로그램언어 고(Go)에서의 기본 고루틴 사용법

고(Go)언어에서 고루틴(goroutine)은 가벼운 스레드와 비슷한 개념입니다. 고루틴을 사용하면 병렬 처리가 가능한 코드를 쉽고 효율적으로 작성할 수 있습니다.


package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world") // 고루틴 실행
    say("hello")   // 메인 고루틴
} 

위 코드는 main과 say 두 개의 고루틴을 실행합니다.

main 고루틴에서 go 키워드를 사용하여 say("world")를 고루틴으로 실행합니다.

이제 "hello"와 "world" 문자열이 병렬로 출력됩니다.

고루틴의 장점은 매우 가볍다는 것입니다.

수천 개의 고루틴을 한 번에 실행할 수 있습니다.

고루틴간 통신은 channel을 이용합니다.


package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

위 예제에서 두 개의 고루틴이 주어진 슬라이스를 반으로 나누어 합을 계산합니다.

고루틴 내에서 계산한 값을 channel에 보냅니다.

main 고루틴에서는 이 channel들로부터 값을 받아 더합니다.

이처럼 channel을 사용하여 고루틴들 간 통신이 가능합니다.

고루틴은 병렬 처리에 효과적으로 사용될 수 있습니다.

channel을 이용한 통신으로 고루틴 간 값 전달이 수월합니다.

고루틴의 사용법을 숙지한다면 복잡한 병렬 처리 코드를 쉽고 효율적으로 작성할 수 있습니다.

이상으로 고루틴 사용법에 대한 간략한 설명을 드렸습니다.

좋은 하루 되세요.

프로그램언어 고(Go)에서의 고루틴을 사용한 병렬 프로그래밍

package main

import (
    "fmt"
    "time"
)

func process(i int) {
    fmt.Println("고루틴", i, "실행")
    time.Sleep(1 * time.Second)
    fmt.Println("고루틴", i, "종료")
}

func main() {
    for i := 0; i < 10; i++ {
        go process(i) 
    }

    time.Sleep(10 * time.Second)
    fmt.Println("메인 함수 종료") 
}

프로그램언어 고(Go)에서 고루틴(goroutine)은 경량 스레드라고 볼 수 있습니다.
고루틴을 사용하면 하나의 프로세스에서 병렬적으로 여러 작업을 수행할 수 있습니다.

위 예제 코드를 보면 main 함수에서 for 문을 돌면서 고루틴을 생성합니다.
go 키워드 뒤에 함수를 호출하면 해당 함수는 고루틴으로 동작합니다.

process 함수에서 1초 시간 지연 후에 실행되고 종료되는 것을 출력합니다.
main 함수에서 10개의 고루틴을 생성했기 때문에 10개의 process 함수가 병렬로 실행됩니다.

마지막으로 main 함수가 10초 시간 지연 후 종료됩니다.
이는 고루틴들이 모두 실행 완료될 시간을 주기 위함입니다.

이러한 고루틴을 사용하면 프로그램 내부에 병렬 처리 로직을 구현할 수 있습니다.
CPU 코어를 효율적으로 사용할 수 있고, 효과적으로 대기시간(Waiting Time)을 줄일 수 있습니다.

따라서 고(Go) 언어의 가장 큰 장점 중 하나로 꼽히며, 효과적으로 병렬 처리가 가능한 언어입니다.

프로그램언어 고(Go)의 고루틴 대기 그룹(WaitGroup) 사용법

고루틴 대기 그룹(WaitGroup)은 고(Go) 언어의 동시성을 위한 기능 중 하나입니다. 이는 여러 개의 고루틴들이 모두 작업을 마칠 때까지 기다리게 해주는 역할을 합니다.


var wg sync.WaitGroup

func doWork(wg *sync.WaitGroup) {

  // 작업 시작 전에 카운터 1 증가
  wg.Add(1) 

  // 고루틴에서 수행할 작업
  go func() {
    defer wg.Done() 
    // logic here
  }()

  // 모든 고루틴 작업이 완료될 때까지 기다림
  wg.Wait() 
}

WaitGroup을 사용하려면 다음과 같은 절차를 따릅니다.

1. sync 패키지에서 WaitGroup 구조체를 가져옵니다.

var wg sync.WaitGroup

2. 고루틴을 실행하기 전에 wg.Add(1) 을 호출하여 대기할 고루틴의 개수를 1 증가시킵니다.

3. 각 고루틴 내부의 맨 처음에 wg.Done() 을 호출하여 작업이 완료되었음을 알립니다.

4. 모든 고루틴 작업이 끝날 때까지 wg.Wait() 을 호출하여 기다립니다.

이러한 방식으로 WaitGroup을 사용하면 주요 고루틴에서 별도의 작업을 위한 고루틴을 시작하고, 모든 고루틴이 완료될 때까지 기다릴 수 있습니다.

예를 들어 위의 예제 코드는 doWork() 함수에서 고루틴을 시작합니다.

시작하기 전에 wg.Add(1)로 대기할 고루틴 수를 1로 설정합니다.

그리고 고루틴 내부 첫 부분에 wg.Done()을 호출하여 작업 완료를 알립니다.

마지막으로 doWork() 함수 내에서 wg.Wait()를 호출하여 고루틴이 끝날 때까지 기다립니다.

이를 통해 주 함수와 별도의 고루틴이 동기화되어 실행되도록 할 수 있습니다.

대기 그룹은 고루틴과 채널을 조합하여 동시성 프로그램을 깔끔하게 구현하는 데 매우 유용합니다.

예를 들어 작업 큐를 처리할 때 대기 그룹을 활용할 수 있습니다.

이상 고루틴 대기 그룹(WaitGroup)에 대해 설명드렸습니다.

프로그램언어 고(Go)에서의 고루틴 스케줄링 이해하기

Go 언어의 고루틴은 경량 스레드와 비슷한 개념으로, 병행성 프로그래밍을 가능하게 해줍니다. 고루틴은 Go 런타임에 의해 스케줄링되는데, 이 스케줄링 방식을 잘 이해하는 것이 중요합니다.

Go 언어의 고루틴 스케줄러는 다음과 같은 특징이 있습니다.


// 고루틴 예제 함수
func hello(wg *sync.WaitGroup) {
    
    defer wg.Done()
    fmt.Println("Hello world")
} 

func main() {

    var wg sync.WaitGroup
    wg.Add(1)
    go hello(&wg) // 고루틴 실행
    wg.Wait() // 고루틴 완료 기다림 
}

- M:N 스레딩 모델 사용
- M개의 OS 스레드가 N개의 고루틴을 실행
- 자동 스케줄링
- 런타임이 스케줄링 해줌
- 선입선출 스케줄링
- 오래 기다린 고루틴이 먼저 실행
- 경량
- 스택 사이즈가 작아 메모리 효율적

고루틴간 실행 순서나 동기화를 위해서는 WaitGroup이나 Channel과 같은 동기화 기법이 필요합니다.

WaitGroup을 사용하면 주 고루틴이 다른 고루틴의 작업 완료를 기다리는 것이 가능합니다. Channel을 사용하면 고루틴 간 데이터 교환 및 동기화가 가능합니다.

이와 같이 Go 언어의 고루틴과 스케줄러는 병행성 프로그래밍을 간편하고 효율적으로 구현할 수 있도록 도와줍니다. 적절한 동기화 기법을 이용하면 더욱 안정적인 병행 처리 시스템을 설계할 수 있습니다.

프로그램언어 고(Go)에서의 고루틴과 채널을 함께 사용하기

package main

import "fmt"

func hello(c chan string) {
    c <- "Hello World!"
}

func main() {
    c := make(chan string)

    go hello(c)

    msg := <-c
    fmt.Println(msg)
}

고(Go)언어에서 고루틴(goroutine)은 병렬처리를 위한 가볍고 사용하기 쉬운 쓰레드입니다.
채널(channel)은 고루틴 간에 데이터를 주고받기 위한 통로 역할을 합니다.

예를들어 위의 코드에서 main 함수가 실행되면 hello 함수를 고루틴으로 실행합니다.
hello 함수는 문자열을 채널 c에 보냅니다.
main 함수에서는 c 채널로부터 데이터를 받아 출력합니다.

이처럼 고루틴과 채널을 함께 사용하면 병렬처리가 가능해지고 데이터 교환이 수월해집니다.
고루틴은 채널 없이는 데이터를 교환할 수 없기 때문에 서로 의존관계에 있다고 볼 수 있습니다.

채널을 사용하면 동기화 문제도 쉽게 해결할 수 있습니다.
main 함수가 c <- "Hello World!"를 만나면 값이 전달될 때까지 대기합니다. 즉 데이터 흐름을 제어할 수 있기 때문입니다. 다중 고루틴이 복잡하게 얽혀 있을 때, 채널을 사용하면 정확한 데이터 흐름을 보장할 수 있습니다. 고루틴의 실행 순서를 제어할 수도 있습니다. 이처럼 고루틴과 채널의 조합을 통해 병렬처리, 동기화, 데이터 통신이 보다 쉽고 효과적으로 이루어집니다. Go언어의 강점 중 하나로 꼽히는 부분이 바로 고루틴과 채널을 활용한 병렬처리 기능입니다.

Leave a Comment