동기화 객체 사용하기

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

조건 변수 사용하기

조건 변수는 대기하고 있는 객체 하나만 깨우거나 여러 개를 동시에 깨울 때 사용합니다.

sync 패키지에서 제공하는 조건 변수의 함수는 다음과 같습니다.

  • sync.Cond
  • func NewCond(l Locker) *Cond: 조건 변수 생성
  • func (c *Cond) Wait(): 고루틴 실행을 멈추고 대기
  • func (c *Cond) Signal(): 대기하고 있는 고루틴 하나만 깨움
  • func (c *Cond) Broadcast(): 대기하고 있는 모든 고루틴을 깨움

먼저 대기하고 있는 고루틴을 하나씩 깨워보겠습니다.

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // 모든 CPU 사용

	var mutex = new(sync.Mutex)    // 뮤텍스 생성
	var cond = sync.NewCond(mutex) // 뮤텍스를 이용하여 조건 변수 생성

	c := make(chan bool, 3) // 비동기 채널 생성

	for i := 0; i < 3; i++ {
		go func(n int) {                        // 고루틴 3개 생성
			mutex.Lock()                    // 뮤텍스 잠금, cond.Wait() 보호 시작
			c <- true                       // 채널 c에 true를 보냄
			fmt.Println("wait begin : ", n)
			cond.Wait()                     // 조건 변수 대기
			fmt.Println("wait end : ", n)
			mutex.Unlock()                  // 뮤텍스 잠금 해제, cond.Wait() 보호 종료

		}(i)
	}

	for i := 0; i < 3; i++ {
		<-c // 채널에서 값을 꺼냄, 고루틴 3개가 모두 실행될 때까지 기다림
	}

	for i := 0; i < 3; i++ {
		mutex.Lock()                // 뮤텍스 잠금, cond.Signal() 보호 시작
		fmt.Println("signal : ", i)
		cond.Signal()               // 대기하고 있는 고루틴을 하나씩 깨움
		mutex.Unlock()              // 뮤텍스 잠금 해제, cond.Signal() 보고 종료
	}

	fmt.Scanln()
}

조건 변수는 뮤텍스를 먼저 생성한 뒤 sync.NewCond 함수로 생성합니다. 대기할 때는 고루틴 안에서 Wait 함수를 사용하며, 대기하는 고루틴을 깨울 때는 Signal 함수를 사용합니다. 여기서 Wait 함수 부분은 뮤텍스의 Lock, Unlock 함수로 보호해야 합니다.

여기서는 채널을 사용하여 모든 고루틴이 생성될 때까지 기다린 뒤 차례대로 Signal 함수를 사용하여 대기하고 있는 고루틴을 깨웁니다.

실행 결과

wait begin :  0
wait begin :  1
wait begin :  2
signal :  0
signal :  1
signal :  2
wait end :  0
wait end :  1
wait end :  2


그림 35-2 조건 변수 Signal 함수로 고루틴을 하나씩 깨움

이번에는 대기하고 있는 모든 고루틴을 깨워보겠습니다.

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // 모든 CPU 사용

	var mutex = new(sync.Mutex)    // 뮤텍스 생성
	var cond = sync.NewCond(mutex) // 뮤텍스를 이용하여 조건 변수 생성

	c := make(chan bool, 3) // 비동기 채널 생성

	for i := 0; i < 3; i++ {
		go func(n int) {                        // 고루틴 3개 생성
			mutex.Lock()                    // 뮤텍스 잠금, cond.Wait() 보호 시작
			c <- true                       // 채널 c에 true를 보냄
			fmt.Println("wait begin : ", n)
			cond.Wait()                     // 조건 변수 대기
			fmt.Println("wait end : ", n)
			mutex.Unlock()                  // 뮤텍스 잠금 해제, cond.Wait() 보호 종료

		}(i)
	}

	for i := 0; i < 3; i++ {
		<-c // 채널에서 값을 꺼냄, 고루틴 3개가 모두 실행될 때까지 기다림
	}

	mutex.Lock()             // 뮤텍스 잠금, cond.Broadcast() 보호 시작
	fmt.Println("broadcast")
	cond.Broadcast()         // 대기하고 있는 모든 고루틴을 깨움
	mutex.Unlock()           // 뮤텍스 잠금 해제, cond.Signal() 보고 종료

	fmt.Scanln()
}

마찬가지로 대기할 때는 고루틴에서 Wait 함수를 사용합니다. 대기하고 있는 모든 고루틴을 깨울 때는 Broadcast 함수를 사용하면 됩니다.

여기서는 Broadcast 함수를 사용한 즉시 대기하고 있던 고루틴 3개가 깨어납니다.

실행 결과

wait begin :  1
wait begin :  2
wait begin :  0
broadcast
wait end :  2
wait end :  1
wait end :  0


그림 35-3 조건 변수 Broadcast 함수로 모든 고루틴을 깨움


저작권 안내

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

Published

01 June 2015