TIL: GoRoutine

고루틴을 배워보자!
게시 날짜:
go TIL

가장 빨리 만나는 Go언어 책의 고루틴 파트를 읽고, 혼자 이해한 내용을 정리한 것입니다. 이 글의 내용은 책의 내용과 매우 유사합니다. 내용의 정확성에 대해서는 보장하지 않습니다.

GoRoutine은 함수를 동시에 실행시키는 기능이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
    "fmt"
)

func aloha() {
    fmt.Println("알로하!")
}

func main() {
    go aloha()

    fmt.Scanln()
}
output:
알로하!

멀티코어 사용하기

Go언어는 원래 CPU코어를 하나만 사용하지만, 사용하는 그 수를 설정하여 늘릴 수 있다.

1
2
runtime.GOMAXPROCS(runtime.NumCPU()) // NumCPU() -> 컴퓨터의 CPU 개수를 리턴한다
                                     // GOMAXPROCS(n) -> n개의 CPU를 사용

runtime.GOMAXPROCS(0)은 현 설정 값을 리턴한다.

클로저를 고루틴으로 실행하기

함수 안에서 클로저를 정의한 뒤 고루틴으로 실행한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
    "fmt"
)

func main() {
    s := "세상"
    for i := 0; i < 100; i++ {
        go func(n int) {
            fmt.Println(s, n)
        }(i)
    }

    fmt.Scanln()
}
output:
세상 99
세상 0
세상 1
세상 2
세상 3
세상 4
세상 5
세상 6
세상 7
세상 8
세상 9
...
세상 98

왜 책의 결과와 다르게 99가 먼저 출력되는 지 모르겠다…

만약 i를 인자로 받지 않고 그대로 사용한다면, 의도했던 대로 출력되지 않는다. CPU를 여러 개 사용해도 의도대로 나오지 않는다.

채널 사용하기

채널은 고루틴끼리 데이터를 주고 받고 실행 흐름을 제어한다. 채널은 레퍼런스 타입이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
    "fmt"
)

func minus(a, b int, c chan int) {
    c <- a - b // a - b의 결과를 채널 c로 보냄
}

func main() {
    c := make(chan int)
    go minus(1, 2, c)
    fmt.Println(<-c)
}
output:
-1

채널의 값을 이미 받았을 때, 어떤 다른 함수도 이 채널에 값을 보내려하지 않으면 데드락이 발생한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import (
	"fmt"
)

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

	go func(c chan int) {
		fmt.Println("고루틴:", <-c)
		c <- 2
	}(c)

	c <- 1
	fmt.Println(<-c) // c에서 값을 받음
	c <- <-c + 1 // DEADLOCK: 이미 c에서 값을 받아 남은 값이 없음
}
output:
고루틴: 1
2
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
	/Users/Bombwhale/deadlock.go:17 +0x10e
exit status 2

내용을 지속적으로 추가할 예정입니다.