본문 바로가기
IT 초보코딩의 세계/Go 언어

Go언어의 조건변수, Once 사용법, Pool 구조체 3장

by 조이럭키7 2023. 4. 5.
반응형

앞서서 채널과 Select구문 그리고 Sync 를 보지 않았다면 아래 포스팅을 숙지후에 3장을 보도록 하자

https://joylucky7.tistory.com/34

 

Go언어의 채널(Channel), Select 구문, Sync 패키지 2장

동시성과 고루틴, 데이터 공유에 대해서 학습하지 않았다면 아래포스팅을 다시한번 보고 오자. https://joylucky7.tistory.com/33 Go언어의 동시성 과 Goroutine, 데이터공유 1장 ◆ 동시성(Concurrency) 란? ▶

joylucky7.tistory.com


◆ 조건변수 

▶ 조건 변수 구조체

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

▶ 대기 중인 고루틴 모든 고류틴 깨우기

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

◆ Sync 패키지 1번만 수행

▶ Once 이용

     ● sync.Once

     ● func (*Once) Do(f func( )): 함수를 한 번만 실

package main

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

func Func() {
  fmt.Println("Hello Once!")
}

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

  once := new(sync.Once) // Once 생성

  for i := 0; i < 3; i++ {
          go func(n int) {   // 고루틴 3개 생성
                fmt.Println("goroutine : ", n)

  once.Do(Func) // 고루틴은 3개지만 hello 함수를 한 번만 실행
  }(i)
  }

  fmt.Scanln()
}

◆ Pool

풀은 객체(메모리) 사용한 후 보관해 두었다가 다시 사용하게 해주는 기능

객체를 반복해서 할당하면 메모리 사용량도 늘어나고 메모리를 해제해야 하는 가비지 컬렉터에게도 부담이 됨

풀은 일종의 캐시라고 할 수 있으며 메모리 할당과 해제 횟수를 줄여 성능을 높이고자 할 때 사용

풀은 여러 고루틴에서 동시에 사용할 수 있음

풀 관련 구조체 와 함수

     ●nsync.Pool

     ● func (p *Pool) Get() interface{}: 풀에 보관된 객체를 가져옴

     ● func (p *Pool) Put(x interface{}): 풀에 객체를 보관

package main

import (
  "fmt"
  "math/rand"
  "runtime"
  "sync"
)

type Data struct { // Data 구조체 정의
  tag    string  // 풀 태그
  buffer []int   // 데이터 저장용 슬라이스
}

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

  pool := sync.Pool{                            // 풀 할당
       New: func() interface{} {             // Get 함수를 사용했을 때 호출될 함수 정의
                data := new(Data)             // 새 메모리 할당
               data.tag = "new"              // 태그 설정
               data.buffer = make([]int, 10) // 슬라이스 공간 할당
               return data                   // 할당한 메모리(객체) 리턴
  },
  }

  for i := 0; i < 10; i++ {
          go func() {                                          // 고루틴 10개 생성
                   data := pool.Get().(*Data)                   // 풀에서 *Data 타입으로 데이터를 가져옴
                   for index := range data.buffer {
                                    data.buffer[index] = rand.Intn(100)  // 슬라이스에 랜덤 값 저장
                    }
                    fmt.Println(data)                            // data 내용 출력
                    data.tag = "used"                            // 객체가 사용되었다는 태그 설정
                    pool.Put(data)                               // 풀에 객체를 보관
          }()
  }

  for i := 0; i < 10; i++ {
         go func() {                               // 고루틴 10개 생성
                  data := pool.Get().(*Data)        // 풀에서 *Data 타입으로 데이터를 가져옴
                  n := 0
                  for index := range data.buffer {
                                 data.buffer[index] = n    // 슬라이스에 짝수 저장
                                 n += 2
                  }
                  fmt.Println(data)                 // data 내용 출력
                  data.tag = "used"                 // 객체가 사용되었다는 태그 설정
                  pool.Put(data)                    // 풀에 객체 보관
  }()
  }
  fmt.Scanln()
}


다음 포스팅에서는 WaitGroup 와 원자적 연산에대해서 알아보는 시간을 가져보자

반응형

댓글