저작권 안내
- 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
- 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.
동기화 객체 사용하기
이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07
원자적 연산 사용하기
원자적 연산은 더 이상 쪼갤 수 없는 연산이라는 뜻입니다. 따라서 여러 스레드(고루틴), CPU 코어에서 같은 변수(메모리)를 수정할 때 서로 영향을 받지 않고 안전하게 연산할 수 있습니다. 보통 원자적 연산은 CPU의 명령어를 직접 사용하여 구현되어 있습니다.
고루틴을 사용하여 정수형 변수를 2,000번은 더하고, 1,000번은 빼보겠습니다.
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // 모든 CPU 사용
var data int32 = 0
wg := new(sync.WaitGroup)
for i := 0; i < 2000; i++ {
wg.Add(1)
go func() { // 고루틴 2,000개 생성
data += 1 // 1씩 더함
wg.Done()
}()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() { // 고루틴 1,000개 생성
data -= 1 // 1씩 뺌
wg.Done()
}()
}
wg.Wait()
fmt.Println(data)
}
실행 결과
948 (매번 달라질 수 있음)
실행해보면 0 + 2000 - 1000은 1000이 되어야하는데 그렇지가 않습니다(실행할 때마다, 시스템마다 실행 결과는 달라질 수 있습니다). 여러 변수에 고루틴이 동시에 접근하면서 정확하게 연산이 되지 않았기 때문입니다.
이번에는 원자적 연산을 사용하여 계산해보겠습니다.
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // 모든 CPU 사용
var data int32 = 0
wg := new(sync.WaitGroup)
for i := 0; i < 2000; i++ {
wg.Add(1)
go func() { // 고루틴 2,000개 생성
atomic.AddInt32(&data, 1) // 원자적 연산으로 1씩 더함
wg.Done()
}()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() { // 고루틴 1,000개 생성
atomic.AddInt32(&data, -1) // 원자적 연산으로 1씩 뺌
wg.Done()
}()
}
wg.Wait()
fmt.Println(data)
}
실행 결과
1000
이제 정확하게 1000이 출력됩니다.
원자적 연산에는 메모리 주소와 수정할 값을 넣습니다. 따라서 atomic.AddInt32(&data, 1)처럼 & (참조)를 사용하여 data 변수의 메모리 주소를 대입합니다.
다음은 sync/atomic 패키지에서 제공하는 원자적 연산의 종류입니다.
- Add 계열: 변수에 값을 더하고 결과를 리턴합니다.
- CompareAndSwap 계열: 변수 A와 B를 비교하여 같으면 C를 대입합니다. 그리고 A와 B가 같으면 true, 다르면 false를 리턴합니다.
- Load 계열: 변수에서 값을 가져옵니다.
- Store 계열: 변수에 값을 저장합니다.
- Swap 계열: 변수에 새 값을 대입하고, 이전 값을 리턴합니다.
저작권 안내
이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.- 블로그, 게시판 등에 퍼가는 것을 금지합니다.
- 비공개 포스트에 퍼가는 것을 금지합니다.
- 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
- 링크 및 SNS 공유는 허용합니다.