Go 에러, 예외 처리, 고루틴, 채널 써보기
2019-02-17 12:13:40

에러

JS와 다르게 에러 인자는 마지막

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import (
"fmt"
"strconv"
"log"
)

func main() {
var thisStr = "qq"

strToInt, err := strconv.Atoi(thisStr) // string -> int 이 함수는 2가지를 리턴함

if err == nil {
fmt.Println(strToInt)
} else {
// log.Fatal(err.Error()) // 메세지 출력하고 프로세스 종료.
log.Print(err.Error()) // 메세지만 출력.
}
}

에러를 의도적으로 만들 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"errors"
)

func main() {
minusSample2, err := sample1(1, 5) // err test

if err == nil {
fmt.Println(minusSample2)
} else {
log.Print(err.Error()) // 2019/02/16 15:19:51 first is small then second
}
}

func sample1(first int, second int) (int, error) {
if first >= second {
return first - second, nil
} else {
return 0, errors.New("first is small then second")
}
}

예외 처리

go에는 try/catch가 업다.

go에서는 에러가 나면 내장되잇는 panic() 함수가 호출이 된다. 그러면 실행하는 함수에서 멈추고

종료된다. recover()라는 내장 함수를 호출 하면 panic 상태를 다시 정상으로 롤백 함.

defer는 try/catch/finally 에서 마지막 finally와 같음.

아직은 뭔가 어색한 느낌이다 -_-

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
31
32
33
34
package main

import (
"fmt"
"time"
"log"
)

func main() {
ex(func() {
fmt.Println("division 1", sample2(2, 0)) // 2019/02/17 17:29:54 runtime error: integer divide by zero
})

ex(func() {
fmt.Println("division 2", sample2(3, 1))
})
}

func sample2(first int, second int) int {
return first / second
}

func ex(f func()) { // 실행할 함수를 파라미터로 받는것.
startTime := time.Now() // 시작 시간
defer func() {
elapsedTime := time.Since(startTime)
fmt.Println("경과시간", elapsedTime)
if err := recover(); err != nil {
log.Print(err)
}
}()

f()
}

고루틴

Go 런타임에서 관리하는 쓰레드이다

OS 쓰레드보다 가볍게 비동기 동시성 처리를 구현하기 위해 만든 것

일반 함수 앞에 키워드로 go 붙이면 끝.

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

import "fmt"

func main() {
test("sync") // 함수를 동기적으로 실행

go test("async") // 고루틴으로 함수를 비동기적으로 실행.
}

func test(param string) {
fmt.Println(param)
}

근데 실행하면 고루틴 함수는 출력이 되지 않는다. 이유는 고루틴은 비동기이기 때문에

메인 함수가 먼저 실행한 뒤 종료되기 때문에 고루틴 함수는 씹힌다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import (
"fmt"
"time"
)

func main() {
test("sync-2")

go test("async-2")

time.Sleep(time.Second * 1) // 1초 대기.
}

func test(param string) {
fmt.Println(param)
}

이런식으로 하면 메인이 실행하고 종료하기까지 1초를 기다리기 때문에 고루틴이 실행이 된다.

익명함수

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
package main

import (
"fmt"
"sync"
)

func main() {

var wait sync.WaitGroup // 모든 고루틴이 종료될 때까지 대기해야 할 때 사용
wait.Add(3) // 3개의 고루틴 대기.

go func() {
defer wait.Done() // 현재 고루틴이 종료되면 호출
fmt.Println("golang")
}()

go func() {
defer wait.Done()
fmt.Println("Javascript")
}()

go func() {
defer wait.Done()
fmt.Println("Python")
}()

wait.Wait() // 위의 익명함수 고루틴들이 끝날 때까지 대기.
}

채널

채널을 통해 고루틴 함수끼리 또는 메인 함수와 데이터를 주고 받을 수 잇다.

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

import "fmt"

func main() {

ch := make(chan int)

go func() {
ch <- 123456 // 채널에 값 보내기.
}()

i := <-ch // 채널로부터 값 받음.

fmt.Println(i) // 123456
}

채널을 쓰게 되면 고루틴 함수가 종료될 때까지 대기하거나 그럴 필요가 업다.

채널을 사용하게 되면 송신자, 수신자가 기다리는 속성이 있어서 데이터 수신이 끝날때까지 기다리기 때문이다.

아 그리고 채널을 파라미터로 써서 함수가 해당 채널을 송신할 것인지, 수신할 것인지 지정할 수 있다.

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
31
32
33
package main

func main() {
ch2 := make(chan string, 1) // 뒤에 숫자만큼 (버퍼) 채널을 생성한다는 뜻.

send(ch2)

response(ch2) // send data

ch3 := make(chan int, 2) // 2개의 버퍼를 가진 채널을 할당.

ch3 <- 10

ch3 <- 20

close(ch3) // 채널 닫기 더이상 송신은 안되지만 수신은 가능하다.

// fmt.Println(<-ch3)
// fmt.Println(<-ch3)

for i := range ch3 { // 채널은 반복문으로도 돌릴수 있군..
fmt.Println(i) // 10 -> 다음은 20 출력.
}
}

func send(ch chan<- string) {
ch <- "send data"
}

func response(ch <-chan string) {
data := <-ch
fmt.Println(data)
}

select

select를 통해 여러 개의 채널로부터 메세지를 기다릴수 있다.

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
31
32
33
package main

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

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

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

go func() {
time.Sleep(time.Second * 5)
c3 <- "3번 말"
}()

for index := 0; index < 3; index++ {
select {
case msg1 := <-c1:
fmt.Println("1번 말 도착", msg1)
case msg2 := <-c2:
fmt.Println("2번 말 도착", msg2)
case msg3 := <-c3:
fmt.Println("3번 말 도착", msg3)
}
}
}

각각 채널을 생성하고 각각의 고루틴에서 쓰인다고 가정하자. (위의 처럼)

이제 반복문에서 select를 통해 각각의 고루틴에서 할당한 채널을 모두 읽게 된다.

위의 경우에 일정 딜레이를 주었지만 생략하거나 중간에 종료되지 않는다. 대기했다가 수신한다.

CPU

Go도 기본적으로 cpu를 1개만 쓴다. 모든 cpu를 활용하려면 아래와 같이 해야 함.

1
2
3
4
5
6
7
8
9
10
package main

import "runtime"

func main() {
thisComputerCPU := runtime.NumCPU() // 현재 컴의 시피유 개수를 구하는 내장 함수.
fmt.Println("CPU 개수:", thisComputerCPU) // 로컬은 인텔 i5인데 4개 나온다 나중에 AWS에서 테스트해보자.

runtime.GOMAXPROCS(thisComputerCPU) // 현재 컴의 시피유를 다 쓰겟다는 거.
}
Prev
2019-02-17 12:13:40
Next