[Go] data race์ mutex, atomic
Go๋ ๊ณ ๋ฃจํด์ ๊ธฐ๋ฐ์ผ๋ก ์ฑ๋ฅ์ ์ผ๋ก ๋นผ์ด๋ ๋น๋๊ธฐ ์์คํ ์ ์ ๊ณตํ๊ธฐ๋ ํ์ง๋ง, ๋๊ธฐํ์ ๋ํ ์์ ์ฑ์ ์๊ฒฉํ์ง๋ ์๋ค. Rust์ฒ๋ผ ๋นก์ธ๊ฒ ๋์์ฑ์ ๊ฒ์ฌํ์ง๋ ์๊ธฐ ๋๋ฌธ์ ๋ณ ์๊ฐ์์ด ์ง๋ค๊ฐ๋ ๋์์ฑ ๋ฒ๊ทธ๊ฐ ๋๊ธฐ ์ฝ๋ค.
์ด๋ฐ ๊ฒ์ ๋ํ ๋ํ์ ์ธ ์ฌ๋ก ์ค ํ๋๊ฐ data race์ผ๊ฒ ๊ฐ๋ค.
์๋ ์ฝ๋๋ data race๊ฐ ๋ฐ์ํ๋ ๊ฐ๋จํ ์ฝ๋๋ค.
๊ณ ๋ฃจํด 10๊ฐ๋ฅผ ๋๋ ค์ ๋จ์ผ ๊ณต์ ๋ณ์๋ฅผ ์ฆ๊ฐ์ํค๋ ๋จ์ํ ๋ก์ง์ ๊ฐ๊ณ ์๋ค.

package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var shared = 0
// ๊ณ ๋ฃจํด 10๊ฐ ๋์ฐ๊ธฐ
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// ๊ฐ ๊ณ ๋ฃจํด์ ๊ณต์ ๋ณ์๋ฅผ 10000๋ฒ์ฉ ์ฆ๊ฐ์ํด
for j := 0; j < 10000; j++ {
shared++
}
}()
}
wg.Wait()
fmt.Println(shared) // 10 * 10000 = 100000๊ฐ ๋์์ผ ํ๋๋ฐ...?
}
์ฐ๋ฆฌ์ ๊ธฐ๋๋๋ก๋ผ๋ฉด ๋น์ฐํ 10๋ง์ด ์ฐํ์ผ ํ๊ฑด๋ง
10๋ง์ด ๋์ค์ง๋ ์๊ณ , ์ผ์ ํ ๊ฐ์ด ๋์ค์ง๋ ์๋๋ค.
์ด๊ฑธ ๋ฐ์ดํฐ ๋ ์ด์ค๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ, ์ฌ๋ฌ๊ฐ์ ์ค๋ ๋๊ฐ ๋์์ ํ๋์ ๋ณ์์ ์ ๊ทผํ๋๊น, ๊ฐ์์ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๊ฐ ์๋ก ์ถฉ๋ํ๋ฉด์ ์๋ก์๋ก ๋ฎ์ด์์๋ฒ๋ฆฌ๋ ๊ฒ์ด๋ค.
์ด๊ฑธ ํด๊ฒฐํ๋ ค๋ฉด ๋๊ธฐํ ๊ฐ๋
๋ค์ ์ ์ฉํด์ผ ํ๋ค.
์ฌ์ค ์ด์ ๋ํ ๊ฐ์ฅ ๊ฐํธํ ํด๊ฒฐ์ฑ
์ ์ฑ๋์ด๋ค.
https://blog.naver.com/sssang97/223176685609
ํ์ง๋ง ์ฑ๋์ ์์ ์์ ์ก์ ์๊ฐ ๋ช
ํํ๊ฒ ๊ตฌ๋ถ๋๋ ์ผ์ด์ค์๋ง ์ ํฉํ๊ณ , ๊ฐ ์ค๋ ๋๋ค์ด ์ฝ๊ธฐ/์ฐ๊ธฐ๋ฅผ ๋ฅ๋์ ์ผ๋ก ํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉํ๊ธฐ๊ฐ ๊ณค๋ํ๋ค. ์ด๋ด ๋๋ ์ง์ ๋ฝ์ ๊ฑธ๊ฑฐ๋ ํด์ผ ํ๋ค.
Mutex
Mutex๋ ์ธ์ด๋ฅผ ์ด์ํด์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋๊ธฐํ ๊ธฐ์ ์ด๋ค.
https://blog.naver.com/sssang97/223131650074
๊ทธ๋ฅ ๋ฌด์ํ๊ฒ ๋ฝ๊ฑธ๊ณ ๋ค๋ฅธ๋์ด ์ ๊ทผํ์ง ๋ชปํ๊ฒ ํ๋๊ฒ ๋ค๋ค.
๊ฐ๋จํ๊ณ ๊ฐ๋ ฅํ์ง๋ง, ๋๋ฌด ๋จ๋ฐํ๋ฉด ์ฑ๋ฅ ์ ํ๊ฐ ์ฌํ ์ ์๊ณ , ๋ฐ๋๋ฝ์ ๋ฌธ์ ๋ ํญ์ ๊ณ ๋ คํด์ผ ํ๋ค.
์๋ ์ฝ๋์์๋ mutex๋ฅผ ์ ์ฉํด์ data race๋ฅผ ํด๊ฒฐํ๋ค.
์ฐ์ ๋ฎคํ ์คํ ๊ณต์ ์ฉ์ผ๋ก ์ ์ธํ ๋ค์์

์ฐ๊ธฐ๋ฅผ ํ ๋๋ง๋ค ๋ฝ์ ๊ฑธ๊ณ ํ๋๋ก ํ๋ค.

package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var shared = 0
var mutex = sync.Mutex{} // ๊ณต์ ๋ณ์๋ฅผ ๋ณดํธํ ๋ฎคํ
์ค ์์ฑ
// ๊ณ ๋ฃจํด 10๊ฐ ๋์ฐ๊ธฐ
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// ๊ฐ ๊ณ ๋ฃจํด์ ๊ณต์ ๋ณ์๋ฅผ 10000๋ฒ์ฉ ์ฆ๊ฐ์ํด
for j := 0; j < 10000; j++ {
mutex.Lock() // ๊ณต์ ๋ณ์ ๋ณดํธ ์์
shared++
mutex.Unlock() // ๊ณต์ ๋ณ์ ๋ณดํธ ์ข
๋ฃ
}
}()
}
wg.Wait()
fmt.Println(shared) // 10 * 10000 = 100000๊ฐ ๋์์ผ ํ๋๋ฐ...?
}
๊ทธ๋ผ ์ฝ๊ฐ ๋๋ ค์ง๊ธด ํ์ง๋ง ์๋๋๋ก ๋์์ ํ๋ค.

atomic
mutex lock์ผ๋ก ์ธํ ์ฑ๋ฅ ๋ณ๋ชฉ์ด ์ฌ๊ฐํ๊ฒ ์ฐ๋ ค๋ ๊ฒฝ์ฐ์๋, atomic ๊ธฐ๋ฅ๋ค์ ์์ฉํ๋๊ฒ ์ข์ ์ ์๋ค.
์ด๊ฑด ๋ฌด์ํ๊ฒ ๋ฝ์ ๊ฑฐ๋๊ฒ ์๋๊ณ , CPU ์ธ์คํธ๋ญ์
์์ค์์ ์ ๊ณตํ๋ ํน์ ๊ธฐ์ ์ ํ์ฉํด์ ์ถฉ๋์ ๋ฐฉ์งํ๋ ๊ธฐ์ ์ด๋ค.
๋ด๊ฐ ์๊ธฐ๋ก ๋ด์ฅ atomic ๊ธฐ๋ฅ์ Compare And Swap์ ๊ธฐ๋ฐ์ผ๋ก atomic์ ์ ๊ณตํ๋ค. ํ์คํ์ง๋ ์๋ค.
https://blog.naver.com/sssang97/222721792446
์๋์์๋ atomic์ ํ์ฉํด์ data race๋ฅผ ์ ๊ฑฐํด๋ดค๋ค.

๊ทธ๋ฅ ๋ง์ ์ด๋ ํ์ ์์ฒด๋ฅผ atomic operation์ผ๋ก ์ฒ๋ฆฌํ๊ฒ ํ๋ ๊ฒ์ด๋ค.

package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var wg sync.WaitGroup
var shared int32 = 0
// ๊ณ ๋ฃจํด 10๊ฐ ๋์ฐ๊ธฐ
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// ๊ฐ ๊ณ ๋ฃจํด์ ๊ณต์ ๋ณ์๋ฅผ 10000๋ฒ์ฉ ์ฆ๊ฐ์ํด
for j := 0; j < 10000; j++ {
atomic.AddInt32(&shared, 1)
}
}()
}
wg.Wait()
fmt.Println(shared) // 10 * 10000 = 100000๊ฐ ๋์์ผ ํ๋๋ฐ...?
}
๊ทธ๋ผ ๊ธฐ๋ํ๋๋๋ก ๋น ๋ฅด๊ฒ๋ ๋์ค๊ณ , ์ฑ๋ฅ๋ ์ถฉ๋ถํ ๋์ฌ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์๋ ๊ทธ๋ฅ ์ฐ๊ธฐ๋ง ๋ฌด์ํ๊ฒ ํด์ atomic์ด ์ข์๋ณด์ผ ์๋ ์๋๋ฐ, ํ์
์ด ๋ณต์กํ๊ฑฐ๋ ์ฝ๊ธฐ์ ์ฐ๊ธฐ, ๊ฒ์ฆ ๋ฑ์ด ํ์ํ ๊ฒฝ์ฐ์๋ atomic์ผ๋ก๋ ํ๊ณ๊ฐ ์์ ์๋ ์๋ค.
์ฐธ์กฐ
https://pkg.go.dev/sync/atomic
https://ikcoo.tistory.com/m/214