2020. 4. 1. 17:25ㆍgolang/golang-grammar
goroutine이란
go routine은 함수 및 메소드를 다른 함수 및 메소드와 동시에 사용할 수 있게 해준다. go routine은 상당히 가벼운 thread이다. thread와 비교하면 만들고 사용하는데 적은 비용이든다. 그러므로 go application은 몇천개의 goroutine이 몇천개 동시에 돌아가고 있습니다.
goroutine의 장점
- goroutine은 thread와 비교하여 적은 비용이 든다. goroutine은 몇 kb이며 goroutine은 thread에 비해 application의 요청에 따라 stack의 용량을 늘렸다 줄일 수 있다. thread의 경우 stack사이즈가 고정되어 있다.
- goroutine들은 몇개의 os thread에 다중송신한다. 몇 천개의 goroutine으로 실행되는 프로그램은 오로지 하나의 thread로 이루어져있다. 만약 goroutine이 입력때문에 blocking되어 있다면 다른 os Thread를 만들어서 남아있는 goroutine을 새로 만든 thread에 옮겨버린다.
- goroutine들 사이에는 channel을 통해 의사소통한다. channel은 의도적으로 goroutine을 사용하여 공유 메모리에 접근할 때 경쟁상태를 예방한다.
goroutine 사용해보기
일반적인 함수의 경우 아래의 코드와 같이 순차적으로 실행된다.
package main
import (
"fmt"
)
func main() {
human1 := [2]string{"sang", "hoon"}
human2 := [2]string{"Hyun", "Jae"}
humanName(human1)
humanName(human2)
}
func humanName(names [2]string) {
for _, name := range names {
fmt.Println(name)
}
}
// sang
// hoon
// Hyun
// Jae
사진으로 보면 아래와 같이 순서대로 실행됩니다.
goroutine을 사용하면 다른 함수들을 동시에 같이 사용할 수 있다.
package main
import (
"fmt"
"time"
)
func main() {
human1 := [2]string{"sang", "hoon"}
human2 := [2]string{"Hyun", "Jae"}
go humanName(human1)
humanName(human2)
}
func humanName(names [2]string) {
for _, name := range names {
fmt.Println(name)
time.Sleep(time.Second)
}
}
//Hyun
//sang
//Jae
//hoon
여기서 time.Sleep()을 사용하는 이유는 go keyword로 지정한 코드가 실행될 충분한 시간을 주는 것이다.
추가로 go로 지정된 함수 및 저기서는 main이 종료되게 되면 go keyword가 있어도 바로 종료되게 된다. 만약 go 만적힌 함수만 실행하게 된다면 아무 출력없이 바로 실행될 것입니다.
다중 goroutine 사용해보기
package main
import (
"fmt"
"time"
)
func numbers() {
for i := 1; i <= 5; i++ {
time.Sleep(250 * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(400 * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep(3000 * time.Millisecond)
fmt.Println("main terminated")
}
// 1 a 2 3 b 4 c 5 d e main terminated
numbers 함수와 alphabets 함수를 서로 다른 시간 간격으로 실행된다.
Channel
channel은 goroutine들 끼리 소통할 수 있게 해주는 매개체이다. channel을 통해 값을 보내면 받는 쪽에서 값이 올때까지 blocking이 걸린다. 그래서 time.Sleep 같은 것이 없어도 channel을 통해 값을 받을 수 있다.
channel 생성
channel := make( chan bool)
예제 코드
package main
import (
"fmt"
"reflect"
)
func isNumbers(numbers int, c chan bool) {
if reflect.TypeOf(numbers).String() == "int" {
c <- true
} else {
c <- false
}
}
func main() {
c := make(chan bool)
numbers := [4]int{1, 1, 3, 2}
for _, number := range numbers {
go isNumbers(number, c)
fmt.Println(<-c)
}
}
c라는 channel을 만들어준 후 <-c 부분을 만나면 c가 값을 받을 때까지 blocking됩니다. 그래서 일반적인 goroutine과 달리 바로 종료되지 않습니다. 값을 넣을 때는 c <- true와 같이 넣어줍니다.
함수에서 receiver를 다음과 같이 설정해주면 send 전용 channel을 만들 수 있다.
package main
import "fmt"
func sendData(sendch chan<- int) {
sendch <- 10
fmt.Println(<- sendch) // Error
}
func main() {
chnl := make(chan int)
go sendData(chnl)
fmt.Println(<-chnl)
}
//10
또한 하나의 변수를 추가하여 성공적으로 receive했나 확인할 수 있다.
close and range
더 이상 받을 값이 없는 경우 close를 receiver에게 알릴 수 있다. close를 통해 value의 상태 값들도 받을 수 있다.
v, ok := <- ch
ok가 true면 성공적으로 값을 받은 것이고 false면 실패한 것이다. 혹은 range를 사용해 받은 만큼 값들을 반복문을 돌려 값을 이용할 수 있다.
package main
import (
"fmt"
)
func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("Received ",v)
}
}
'golang > golang-grammar' 카테고리의 다른 글
goroutine deadlock (0) | 2020.04.01 |
---|---|
Slice and Array (0) | 2020.03.30 |
go 문법 part5 (goroutine, channel, Select) (0) | 2020.03.27 |
golang part4(Method, Pointer and Method, Interface, Stringers, Error, Readers) (0) | 2020.03.24 |
go 문법 part3(pointer, struct, array, slice, range, map) (0) | 2020.03.20 |