고루틴 사용하기


이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07



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

함수 안에서 클로저를 정의한 뒤 고루틴으로 실행할 수 있습니다. 예제의 출력 결과를 좀 더 확실하게 표현하기 위해 CPU 코어는 하나만 사용하도록 설정합니다.

package main

import (
	"fmt"
	"runtime"
)

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()
}

실행 결과

Hello, world! 0
Hello, world! 1
Hello, world! 2
.. 생략 ..
Hello, world! 97
Hello, world! 98
Hello, world! 99

클로저를 고루틴으로 실행할 때 반복문 안에서 변수 사용에 주의해야 합니다. 예제에서는 반복문으로 증가하는 i를 클로저에서 그대로 사용하지 않고, 매개변수로 넘겨주었습니다. 일반 클로저는 반복문 안에서 순서대로 실행되지만 고루틴으로 실행한 클로저는 반복문이 끝난 뒤에 고루틴이 실행됩니다.

코드를 수정하여 변수 i를 클로저의 매개변수로 넘기지 않고 fmt.Println으로 바로 출력해봅니다.

func main() {
	runtime.GOMAXPROCS(1)

	s := "Hello, world!"

	for i := 0; i < 100; i++ {
		go func() {
			fmt.Println(s, i) // 반복문의 변수를
                                          // 클로저에서 바로 출력
		}()
	}

	fmt.Scanln()
}

실행 결과

Hello, world! 100
Hello, world! 100
Hello, world! 100
... 생략 ...
Hello, world! 100
Hello, world! 100
Hello, world! 100

이렇게 되면 변수 i는 0부터 99까지 증가하고, 다시 100이 되면서 반복문이 끝납니다. 고루틴은 반복문이 완전히 끝난 다음에 생성되므로 고루틴이 생성된 시점의 변수 i의 값은 100입니다. 따라서 모두 Hello, world! 100이 출력됩니다(CPU 코어를 하나만 사용했을 때의 상황입니다. CPU 코어를 여러 개 사용하면 결과는 조금 달라지지만 의도했던대로 0부터 99까지 빠짐없이 출력되지는 않습니다).

클로저를 고루틴으로 실행할 때 반복문에 의해 바뀌는 변수는 반드시 매개변수로 넘겨줍니다. 즉 매개변수로 넘겨주는 시점에 해당 변수의 값이 복사되므로 고루틴이 생성될 때 그대로 사용할 수 있습니다. 또한, CPU 코어를 하나만 사용하든 여러 개 사용하든 상관없이 반복문에 의해 바뀌는 변수는 매개변수로 넘겨주어야 합니다.

추가 해설
Go 1.4에서는 runtime.GOMAXPROCS(1)를 설정하면 고루틴이 순서대로 실행되지만 Go 1.5 이상부터는 순서대로 실행되지 않을 수도 있습니다. 이번 예제는 고루틴이 순서대로 실행되는지를 알아보는 것이 아니라 클로저를 고루틴을 실행했을 때 변수가 제대로 전달되는지를 알아보는 것이 목적입니다. Go 언어에서 고루틴의 실행 순서를 보장하려면 동기 채널 등을 사용해야 합니다(‘34.1 동기 채널’ 참조).


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015