1. 고루틴
#예제 : 고루틴 100개를 동시에 실행하면서 각 고루틴이 랜덤한 시간동안 대기하는 go함수 작성
import (
"fmt"
"math/rand"
"time"
)
func hello(n int) {
r := rand.Intn(100) //랜덤한 숫자 생성
time.Sleep(time.Duration(r)) //랜덤한 시간 동안 대기
fmt.Println(n)
}
func main() {
for i:=0; i<100; i++ {
go hello(i)
}
fmt.Scanln()
}
# 클로저를 고루틴으로 실행하기
func main(){
runtime.GOMAXPROCS(1) // CPU를 하나만 사용
s := "Hello, World"
for i:=0; i<100; i++ {
go func(n int) { //익명 함수를 고루틴으로 실행(클로저)
fmt.Println(s, n) //s와 매개변수 받은 n값 출력
}(i) //반복문의 변수는 매개변수로 넘겨줌
}
fmt.Scanln()
}
클로저를 고루틴으로 실행할 때 반복문 안에서 변수 사용에 주의해야 한다. 예제에서는 반복문으로 증가하는 i를 클로저에서 그대로 사용하지 않고, 매개변수로 넘겨주었다.
일반 클로저는 반복문 안에서 순서대로 실행되지만 고루틴으로 실행한 클로저는 반복문이 끝난 뒤에 고루틴이 실행된다.
● 클로저를 고루틴으로 실행할 때 반복문에 의해 바뀌는 변수는 반드시 매개변수로 넘겨준다. 즉 매개변수로 넘겨주는 시점에 해당 변수의 값이 복사되므로 고루틴이 생성될 때 그대로 사용할 수 있다. 또한, CPU코어를 하나만 사용하든 여러 개 사용하든 상관없이 반복문에 의해 바뀌는 변수는 매개변수로 넘겨주어야 한다.
2. 채널
● 채널(channel)은 고루틴끼리 데이터를 주고받고, 실행 흐름을 제어하는 기능이다. 채널 자체는 값이 아닌 레퍼런스 타입이다.
# make(chan 자료형)
func sum(a int, b int, c chan int) {
c <- a+b
}
func main() {
/*var c chan int
c = make(chan int)*/
c := make(chan int) //int형 채널 생성
go sum(1,2,c) //sum을 고루틴으로 실행한 뒤 채널을 매개변수로 넘겨 줌
n := <- c //채널에서 값을 꺼낸 뒤 n에 대입
fmt.Println(n)
}
# 동기 채녈
func main() {
done := make(chan bool)
count := 3
go func() {
for i:=0; i<count; i++ {
done <- true
fmt.Println("고루틴 : ", i)
time.Sleep(2 * time.Second)
}
}()
for i:=0; i<count; i++ {
<-done
fmt.Println("메인함수",i )
}
}
● make변수에 매개변수를 하나만 지정했으므로 동기 채널이 생성된다.
# 채널 버퍼링, 비동기 채널
make(chan 자료형, 버퍼개수)
● 채널에 버퍼를 1개 이상 설정하면 비동기 채널이 생성된다. 비동기 채널은 보내는 쪽에서 버퍼가 가득차면 실행을 멈추고 대기하며, 받는 쪽에서는 버퍼에 값이 없으면 대기한다.
func main() {
runtime.GOMAXPROCS(1)
done := make(chan bool, 2) //버퍼가 2개인 비동기 채널 생성
count := 4
go func() {
for i:=0; i<count; i++ {
done <- true
//채널에 true를 보냄, 버퍼가 가득차면 대기
fmt.Println("고루틴 : ", i)
}
}()
for i:=0; i<count; i++ {
<-done //버퍼에 값이 없으면 대기, 값을 꺼냄
fmt.Println("메인함수 : ", i)
}
}
# 보내기 전용, 받기 전용 채널
func producer(c chan<- int){ //보내기 전용 채널
for i:=0; i<5; i++ {
c <-i
}
c<-100 //채널에 값을 보냄
//fmt.Println(<-c) //채널에서 값을 꺼내면 컴파일 에러
}
func consumer(c <-chan int) { // 받기 전용 채널
for i := range c {
fmt.Println(i)
}
fmt.Println(<-c)
//c<-1 //채널에 값을 보내면 컴파일 에러
}
func main() {
c := make(chan int)
go producer(c)
go consumer(c)
fmt.Scanln()
}
-- 출력
0
1
2
3
4
100
# 채널만 사용하여 값 더하기
func num(a, b int) <-chan int {
out := make(chan int)
go func() {
out <- a //채널에 a의 값을 보냄
out <- b //채널에 b의 값을 보냄
close(out)
}()
return out
}
func sum(c <-chan int) <-chan int {
out := make(chan int)
go func() {
r := 0
for i := range c { //range를 사용하여 채널이 닫힐 때까지 값을 꺼냄
r = r + i
}
out <- r
}()
return out
}
func main() {
c := num(1, 2) //1과 2가 들어있는 채널이 리턴됨
out := sum(c) // 채널 c를 매개변수에 넘겨서 모두 더함
//더한 값이 들어있는 out 채널을 리턴
fmt.Println(<-out) // 3
}
# select문
func main() {
c1 := make(chan int)
c2 := make(chan string)
go func(){
for {
c1 <- 10
time.Sleep(100*time.Millisecond)
}
}()
go func(){
for {
c2 <- "hello world"
time.Sleep(500*time.Millisecond)
}
}()
go func() {
for {
select {
case i := <-c1:
// 채널 c1에 값이 들어왔다면 값을 꺼내서 i에 대입
fmt.Println("c1: ", i)
case s := <-c2:
fmt.Println("c2: ", s)
case <-time.After(50*time.Millisecond):
fmt.Println("timeout")
}
}
}()
time.Sleep(10*time.Second)
}
'백엔드, 기타 > Golang' 카테고리의 다른 글
Golang 4. 동기화 객체(Synchronization) (0) | 2020.08.25 |
---|---|
Golang 3. 2)인터페이스 (0) | 2020.08.20 |
Golang 3. 1)포인터와 구조체 (0) | 2020.08.19 |
Golang 2. 함수 2)클로저(closure), 지연호출(defer), 패닉과 복구(panic and recover) (0) | 2020.08.15 |
Golang 2. 함수 1)함수의 리턴값/ 변수,슬라이스,맵에 함수 대응하기/클로저 (0) | 2020.08.15 |