티스토리 뷰

go

Go루틴(goroutine)

까오기 2024. 7. 12. 08:56

Go루틴(Goroutine)은 Go 언어에서 경량 스레드를 구현한 개념으로, 동시에 많은 작업을 효율적으로 처리할 수 있게 해줍니다. Go루틴은 매우 가볍고, 수천 개의 Go루틴을 생성하고 실행하는 것이 일반적입니다.

Go루틴 생성

Go루틴은 go 키워드를 사용하여 생성할 수 있습니다. Go루틴은 함수나 메서드를 비동기적으로 실행합니다.

예제: 간단한 Go루틴 생성

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")
}

결과 

hello
world
world
hello
hello
world
world
hello
hello

위 예제에서 say 함수는 hello와 world 문자열을 5번 출력합니다. main 함수에서 go say("world")를 호출하면 say 함수가 Go루틴으로 실행됩니다. 따라서 "hello"와 "world"가 비동기적으로 출력됩니다.

Go루틴의 동기화

Go루틴은 독립적으로 실행되기 때문에, 여러 Go루틴 간의 동기화가 필요할 수 있습니다. Go에서는 채널(Channels)을 사용하여 Go루틴 간의 통신과 동기화를 처리합니다.

채널

채널은 Go루틴 간의 데이터를 주고받을 수 있는 파이프라인입니다. 채널을 사용하면 Go루틴 간의 통신을 안전하고 쉽게 구현할 수 있습니다.

예제: 채널을 사용한 동기화

package main

import (
    "fmt"
)

func sum(a []int, c chan int) {
    total := 0
    for _, v := range a {
        total += v
    }
    c <- total // total 값을 채널에 전송
}

func main() {
    a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    c := make(chan int)

    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)

    x, y := <-c, <-c // 채널로부터 값을 수신
    fmt.Println(x, y, x+y)
}

위 예제에서 두 개의 Go루틴이 배열의 절반씩 합계를 계산하고, 그 결과를 채널을 통해 전달합니다. main 함수에서는 채널을 통해 결과를 수신하고 합계를 출력합니다.

버퍼링된 채널

채널은 버퍼링될 수 있으며, 이를 통해 비동기적인 데이터 전송이 가능합니다. 버퍼링된 채널은 수신자가 데이터를 받을 준비가 되어 있지 않아도 데이터를 보낼 수 있습니다.

예제: 버퍼링된 채널

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2

    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

위 예제에서 버퍼 크기가 2인 채널을 생성하고, 두 개의 값을 채널에 전송합니다. 이후 채널로부터 두 개의 값을 수신합니다.

select 문

select 문은 여러 채널 작업을 대기할 수 있게 해줍니다. 이는 여러 Go루틴 간의 복잡한 동기화 로직을 쉽게 구현할 수 있게 합니다.

예제: select 문

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("Received", msg1)
        case msg2 := <-c2:
            fmt.Println("Received", msg2)
        }
    }
}
위 예제에서 두 개의 채널이 각각 1초와 2초 후에 값을 전송합니다. select 문을 사용하여 두 채널 중 어느 하나로부터 값을 수신할 때까지 대기합니다.

동기화 프리미티브

Go는 또한 고수준 동기화 프리미티브를 제공하며, 이는 sync 패키지에서 찾을 수 있습니다. 이 패키지에는 WaitGroup, Mutex 및 Cond와 같은 여러 동기화 도구가 포함되어 있습니다.

예제: WaitGroup 사용

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    // 작업 수행
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
}

위 예제에서 WaitGroup을 사용하여 세 개의 Go루틴이 작업을 완료할 때까지 대기합니다.

요약

  • Go루틴: 경량 스레드로, go 키워드를 사용하여 생성됩니다.
  • 채널: Go루틴 간의 통신을 위한 파이프라인입니다. 데이터를 안전하게 주고받을 수 있습니다.
  • 버퍼링된 채널: 비동기적 데이터 전송을 지원합니다.
  • select 문: 여러 채널 작업을 대기할 수 있게 합니다.
  • 동기화 프리미티브: sync 패키지에서 제공하는 고수준 동기화 도구들로, WaitGroup, Mutex 등이 있습니다.

Go루틴과 채널을 잘 활용하면 병렬 처리를 효율적으로 구현할 수 있으며, 동기화를 통해 안정적인 프로그램을 작성할 수 있습니다.

 

GIT : https://github.com/kkaok/study-golang/tree/master/src/example/routine

 

study-golang/src/example/routine at master · kkaok/study-golang

Contribute to kkaok/study-golang development by creating an account on GitHub.

github.com

 

출처 : chatGPT

'go' 카테고리의 다른 글

Go 파일 읽고 쓰기  (0) 2024.07.16
Go 채널(Channels)  (0) 2024.07.12
Go defer, panic, recover  (0) 2024.07.11
Go 에러, 에러처리  (0) 2024.07.11
Go 인터페이스  (0) 2024.07.11
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함