반응형
앞서서 채널과 Select구문 그리고 Sync 를 보지 않았다면 아래 포스팅을 숙지후에 3장을 보도록 하자
https://joylucky7.tistory.com/34
◆ 조건변수
▶ 조건 변수 구조체
● 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 와 원자적 연산에대해서 알아보는 시간을 가져보자
반응형
'IT 초보코딩의 세계 > Go 언어' 카테고리의 다른 글
Go언어의 Package (6) | 2023.04.08 |
---|---|
Go언어의 WaitGroup 와 원자적 연산법 4장 (12) | 2023.04.05 |
Go언어의 채널(Channel), Select 구문, Sync 패키지 2장 (0) | 2023.04.04 |
Go언어의 동시성 과 Goroutine, 데이터공유 1장 (0) | 2023.04.04 |
Go언어의 구조체(포인터, Struct) 1장 (6) | 2023.03.30 |
댓글