개발일기

Go - goroutine 사용법 본문

프로그래밍 언어/Go

Go - goroutine 사용법

Flashback 2022. 9. 3. 14:35
728x90
반응형

Go는 스레드와 비슷한 기능을 가진 고루틴(goroutine)을 제공한다. 고루틴은 함수를 동시에 여러개 실행시킬 수 있는 기능으로써 스레드 보다 언어 문법이 더 간단하며 OS의 리소스를 덜 사용하는 장점을 가지고 있다.

 

1. 호출법

고루틴을 호출 할 때는, 단순히 함수 앞에 go라는 키워드를 붙여주면 된다.

package main
import "fmt"

// 함수 생성
func test() {
    fmt.Println("고루틴 test 함수 실행")
}

func main() {
    fmt.Println("main 함수 실행")
    go test() // test 함수 앞에 go라는 키워드를 붙여준다.
}

/*
    실행 결과 : 
    main 함수 실행
*/

위의 코드를 실행했을 때, 예상하는 결과와는 다르게 main 함수의 출력문만 나오게 된다. 이 이유는 main 함수와 test 함수가 동시에 실행되어 test 함수의 출력문이 시작되기 전에 main 함수가 종료되어 main 함수의 출력문만 나오게 되었다.

고루틴의 정상 작동을 확인하기 위해 time 패키지의 Sleep 함수를 사용한다.

 

  • time.Sleep 함수 : 일정 시간 동안 다음 구문의 실행을 보류한다.

1-1. time.Sleep 사용법

package main

import "fmt"
import "time"

func main() {
    fmt.Println(time.Millisecond)
}

/*
    실행 결과 : 
    1ms
*/

time.Millisecond는 1ms의 단위로 시간을 나타낸다. 즉, 0.001초를 나타내는 것으로 만약 time.Sleep을 통해 실행 대기를 구현하려면 대기시간을 time.Millisecond * 1000 (1초) 으로 지정하면 된다.

 

1-2. 고루틴과 time.Sleep() 함수 사용

package main
import "fmt"
import "time"

func test() {
    fmt.Println("고루틴 test 함수 실행")
}

func main() {
    fmt.Println("main 함수 실행")
    go test()
    time.Sleep(time.Millisecond * 1000) // 1초 대기 한 후에 main 함수가 종료된다.
}

/*
    실행 결과 : 
    main 함수 실행
    고루틴 test 함수 실행
*/

main 함수가 바로 종료되지 않도록 하기 위해 time.Sleep()을 1초로 지정하였다. main 함수가 바로 종료되지 않기 때문에 test 함수의 출력부분도 나오는 것을 확인할 수 있다.

 

2. 익명함수와 고루틴

함수를 정의하지 않고, 익명함수를 통해 고루틴을 활용할 수도 있다.

package main

import "fmt"
import "time"

func main() {

    fmt.Println("main routine start")
  
    // 익명 고루틴 함수
    go func() {
        fmt.Println("go function test")
    }()

    time.Sleep(time.Millisecond * 1000) // main 함수 바로 종료 방지
}

/*
    실행 결과 : 
    main routine start
    go function test
*/

 

3. CPU 코어 다중 사용

고루틴은 스레드와 비슷한 개념이기에 CPU코어를 하나뿐만 아니라 여러개 사용할 수 있다.

package main

import "fmt"
import "runtime" // runtime 패키지 사용

func main() {
    fmt.Println(runtime.NumCPU())
}

/*
    실행 결과 : 
    6 // CPU 코어의 개수는 사용자 컴퓨터의 CPU 코어 수에 따라 결과가 달라진다.
*/

runtime 패키지를 import한 후, runtime.NumCPU() 를 통해 현재 컴퓨터의 CPU 코어 개수를 확인할 수 있다.

 

Go는 기본적으로 CPU 코어를 한 개만 사용되도록 설정되어있다. 하지만 사용자가 사용할 CPU 코어의 수를 runtime.GOMAXPROCS를 통해 임의로 변경하여 사용할 수 있다.

package main

import "fmt"
import "runtime"

func main() {
    runtime.GOMAXPROCS(2) // CPU 코어를 2개 사용하도록 변경
    fmt.Println(runtime.GOMAXPROCS(0)) // 사용하는 CPU 코어의 개수 출력
}

/*
    실행 결과 : 
    2
*/

runtime.GOMAXPROCS 함수 안에 사용할 CPU 코어의 개수를 추가하면 사용할 코어의 개수가 변경된다. 현재 프로그램에서 사용하는 CPU 코어의 개수를 확인하려면 GOMAXPROCS 안에 0을 입력하고 출력한다.

  • runtime.NumCPU() : 현재 컴퓨터의 CPU 코어 개수
  • runtime.GOMAXPROCS(코어 개수 입력) : 현재 프로그램에서 사용할 CPU 코어의 개수 
  • runtime.GOMAXPROCS(0) : 현재 프로그램에서 사용하는 CPU 코어의 개수

 

4. 고루틴 대기그룹

고루틴을 통해 결과를 확인할 때, 메인함수보다 먼저 종료되지 않도록 time.Sleep() 함수를 사용하였다. 하지만 sync 패키지를 사용하여 이를 대체할 수 있으며, 고루틴 관련 더 많은 옵션을 부여할 수 있다.

 

package main

import "fmt"
import "sync" // sync 패키지를 가져온다

func main() {
    fmt.Println("start main func")
    
    wg := new(sync.WaitGroup) // 대기 그룹 생성

    wg.Add(1) // 고루틴을 하나 추가한다.
    go func() {
        fmt.Println("start test goroutine")
        defer wg.Done() // 실행된 고루틴이 종료되었다는 것을 알려준다.
    }()

    wg.Wait() // 실행 중인 고루틴이 모두 종료 될 때 까지 기다린다.
}

/*
    실행 결과 : 
    start main func
    start test goroutine
*/
  • Add() : 대기 그룹에 고루틴 개수를 추가한다.
  • Done() : 고루틴이 종료되었다는 것을 알려준다.
  • Wait() : 모든 고루틴이 끝날 때 까지 대기한다.

wg라는 대기 그룹에 고루틴을 하나 추가한다. 익명함수로 생성된 고루틴이 종료될 때, Done()으로 고루틴의 종료를 대기그룹에 알려준다. Wait()를 통해 대기하고 있던 대기그룹은 Done으로 인해 종료가 되며 익명함수의 내부 출력문도 출력되는 것을 확인할 수 있다.

 

만약 고루틴을 5개 추가하는 wg.Add(5)를 사용한 경우, 고루틴의 개수와 종료되는 개수가 동일하여야 하기 때문에 wg.Done()은 총 다섯번이 호출되어야 한다.

package main

import "fmt"
import "sync"

func main() {
    fmt.Println("start main func")
    
    wg := new(sync.WaitGroup)

    wg.Add(5)
    go func() {
        fmt.Println("start test goroutine")
        defer wg.Done()
    }()

    wg.Wait()
}

/*
    실행 결과 : 
    start main func
    start test goroutine
    fatal error: all goroutines are asleep - deadlock!

    goroutine 1 [semacquire]:
    sync.runtime_Semacquire(0xc000018078)
        /usr/local/go/src/runtime/sema.go:56 +0x42
    sync.(*WaitGroup).Wait(0xc000018070)
        /usr/local/go/src/sync/waitgroup.go:130 +0x64
    main.main()
        /home/runner/ndyj0endjo/main.go:17 +0xd5
    exit status 2
*/

고루틴을 다섯개 추가하였지만 종료는 하나만 한 경우, 위와 같은 패닉 에러가 발생하게 된다.

 


참고 사이트 : 

https://dev-yakuza.posstree.com/en/golang/goroutine/

 

[Golang] Goroutine

Let’s see how to use thread in Golang by Goroutine.

dev-yakuza.posstree.com

 

https://stackoverflow.com/questions/51065482/how-to-test-if-a-goroutine-has-been-called-while-unit-testing-in-golang

 

How to test if a goroutine has been called while unit testing in Golang?

suppose that we have a method like this: func method(intr MyInterface) { go intr.exec() } In unit testing method, we want to assert that inter.exec has been called once and only once; so we ...

stackoverflow.com

 

728x90
반응형
Comments