ํฌ์ŠคํŠธ

CS โ€” race condition

CS โ€” race condition

๐Ÿ“• 05/13 โ€” Race Condition์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐ ํ•ด์ฃผ์„ธ์š”

๋ชจ์˜๋ฉด์ ‘ ์ฃผ์ œ: โ€œRace Condition์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐ ํ•ด์ฃผ์„ธ์š”โ€ ์ •์˜(๊ณต์œ  ์ž์› + ๋™์‹œ ์ ‘๊ทผ + ๋น„๊ฒฐ์ •์„ฑ) โ†’ Critical Section โ†’ ๋™๊ธฐํ™” ๊ฐ์ฒด ์นดํƒˆ๋กœ๊ทธ(MutexยทSemaphoreยทCritical SectionยทSRWLockยทEventยทCondition Variable) โ†’ Lock-free / atomic / CAS โ†’ Memory OrderingยทMemory Barrier(acquire/release) โ†’ ABA ๋ฌธ์ œ โ†’ DeadlockยทLivelockยทStarvation โ†’ Priority Inversion โ†’ Windows/POSIX API ๋น„๊ต โ†’ ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ โ†’ ์–ธ๋ฆฌ์–ผ(GameThread/RenderThread ๋ถ„๋ฆฌยทTaskGraph)๊นŒ์ง€


ํ•™์Šต ์˜์—ญ โ€” ํ”„๋กœ์„ธ์Šค vs ์Šค๋ ˆ๋“œ(19)ยท์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)ยทIPC(22)์—์„œ ํŒŒ์ƒ๋œ ๋™์‹œ์„ฑ ํšŒ๊ท€

ํ”„๋กœ์„ธ์Šค vs ์Šค๋ ˆ๋“œ(19)์—์„œ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ์•ˆ์˜ ์Šค๋ ˆ๋“œ๋Š” ์ฝ”๋“œยท๋ฐ์ดํ„ฐยทํž™์„ ๊ณต์œ ํ•œ๋‹ค๋Š” ์ ์„ ๋ดค๊ณ , ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์—์„œ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž„์˜์˜ ์‹œ์ ์— ๊ฐ•์ œ๋กœ ๊ต์ฒด๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ๋ดค์Šต๋‹ˆ๋‹ค. IPC(22)์—์„  ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ฐ€์žฅ ๋น ๋ฅด์ง€๋งŒ ๋™๊ธฐํ™”๋ฅผ ์ง์ ‘ ํ•ด์•ผ ํ•˜๋Š” ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์งš์—ˆ์Šต๋‹ˆ๋‹ค. 23๋ฒˆ์€ ๊ทธ ์„ธ ์ฃผ์ œ๊ฐ€ ๋ชจ๋‘ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฒฐ๋ก , ๊ณต์œ  ์ž์› + ๋™์‹œ ์ ‘๊ทผ + ๋น„๊ฒฐ์ •์  ์Šค์ผ€์ค„๋ง์ด ๋งŒ๋‚˜๋Š” ์ง€์ ์—์„œ ์ผ์–ด๋‚˜๋Š” Race Condition์ด ๋ณธ ์ฃผ์ œ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
01๋ฒˆ ๋ฉ”๋ชจ๋ฆฌ 4์˜์—ญ (Code/Data/Heap/Stack)        โ† ๊ณต์œ  ์˜์—ญ์˜ ์œ„์น˜
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
19๋ฒˆ ํ”„๋กœ์„ธ์Šค vs ์Šค๋ ˆ๋“œ                          โ† ์Šค๋ ˆ๋“œ๋Š” ํž™ยท์ „์—ญ ๊ณต์œ 
20๋ฒˆ Stack Overflow (์Šค๋ ˆ๋“œ๋ณ„ ๋…๋ฆฝ ์Šคํƒ)         โ† ๊ณต์œ  vs ๋ถ„๋ฆฌ ์˜์—ญ ๊ตฌ๋ถ„
21๋ฒˆ Context Switching (โ˜…)                       โ† ์ž„์˜ ์‹œ์  ์Šค๋ ˆ๋“œ ๊ต์ฒด
22๋ฒˆ IPC (โ˜…)                                     โ† ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋™๊ธฐํ™” ์ง์ ‘ ์ฒ˜๋ฆฌ
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
23๋ฒˆ Race Condition (โ˜…)                          โ† ๋ณธ ์ฃผ์ œ โ€” ๋™์‹œ ์ ‘๊ทผ์˜ ๊ฒฐ๊ณผ ๋น„๊ฒฐ์ •์„ฑ
์ดํ›„ ๋ฝ-ํ”„๋ฆฌ ์ž๋ฃŒ๊ตฌ์กฐ / ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ ์‹ฌํ™” / ํŠธ๋žœ์žญ์…˜ ๋ฉ”๋ชจ๋ฆฌ

Race Condition์€ OS ์ฑ…์˜ ๋™์‹œ์„ฑ ์ฑ•ํ„ฐ์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ๋“ฑ์žฅํ•˜๋Š” ๋‹จ์–ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” CPU ๋งˆ์ดํฌ๋กœ์•„ํ‚คํ…์ฒ˜(๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธยท์บ์‹œ ์ผ๊ด€์„ฑ)ยท์–ธ์–ด ํ‘œ์ค€(C++ memory_order)ยทOS API(MutexยทSRWLock)ยทํ•˜๋“œ์›จ์–ด ๋ช…๋ น(LOCK CMPXCHG) ์ด ๋ชจ๋‘ ๋งŒ๋‚˜๋Š” ์ง€์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฉด์ ‘์—์„œ โ€œRace Condition์ด ๋ญก๋‹ˆ๊นŒโ€๋ฅผ ๋ฌผ์œผ๋ฉด, ์ •์˜ยท์˜ˆ์‹œยทํ•ด๊ฒฐ์ฑ…ยทํ•ด๊ฒฐ์ฑ…์˜ ๋น„์šฉยทํ•ด๊ฒฐ์ฑ… ์‚ฌ์ด์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊นŒ์ง€ 4~5๋‹จ๊ณ„๋กœ ํ’€ ์ˆ˜ ์žˆ์–ด์•ผ ๊นŠ์ด๊ฐ€ ๋“œ๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค.

Windows๋Š” ํŠนํžˆ CRITICAL_SECTIONยทSRWLockยทMutexยทSemaphoreยทEventยทInterlockedExchangeยทstd::atomic๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ๋น„์šฉ์ด ๋‹ค๋ฅธ ๋™๊ธฐํ™” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ โ€œrace๋ฅผ ๋ง‰๋Š”๋‹คโ€๋Š” ์ž‘์—…์ด ์ˆ˜ ns(atomic CAS) ๋ถ€ํ„ฐ ์ˆ˜ ฮผs(์ปค๋„ Mutex) ๊นŒ์ง€ 1000๋ฐฐ ๋น„์šฉ ์ฐจ์ด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Race Condition์„ ๋ง‰๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ โ€œ์–ด๋–ป๊ฒŒ ์ ์€ ๋น„์šฉ์œผ๋กœ ๋ง‰๋А๋ƒโ€ ๊ฐ€ ์ง„์งœ ์—”์ง€๋‹ˆ์–ด๋ง ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.


๋ชจ์˜๋ฉด์ ‘ ๋‹ต๋ณ€

Race Condition์€ ๋‘˜ ์ด์ƒ์˜ ์‹คํ–‰ ๋‹จ์œ„(์Šค๋ ˆ๋“œยทํ”„๋กœ์„ธ์Šค)๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉด์„œ, ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ํ•ต์‹ฌ์€ ์„ธ ๊ฐ€์ง€ ์กฐ๊ฑด์ด ๋ชจ๋‘ ๋งŒ์กฑ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค โ€” โ‘  ๊ณต์œ  ์ž์›์ด ์กด์žฌํ•˜๊ณ , โ‘ก ์ ์–ด๋„ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ๊ทธ ์ž์›์— ์ ‘๊ทผํ•˜๊ณ , โ‘ข ๊ทธ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์ด ์“ฐ๊ธฐ(write)๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, โ‘ฃ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ OS๊ฐ€ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋„ค ์กฐ๊ฑด์ด ๋ชจ๋‘ ๋งŒ์กฑ๋˜์–ด์•ผ race๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ฝ๊ธฐ๋งŒ ํ•˜๋Š” ๋™์‹œ ์ ‘๊ทผ์€ race๊ฐ€ ์•„๋‹ˆ๊ณ , ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ์˜ ์ ‘๊ทผ๋„ race๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

๊ฐ€์žฅ ํ”ํ•œ ์˜ˆ๊ฐ€ counter ์ฆ๊ฐ€์ž…๋‹ˆ๋‹ค. count++๋ผ๋Š” ํ•œ ์ค„์ด CPU ๋ช…๋ น์œผ๋กœ๋Š” load โ†’ increment โ†’ store ์„ธ ๋‹จ๊ณ„๋กœ ๋ถ„ํ•ด๋˜๊ณ , ๊ทธ ์‚ฌ์ด ์–ด๋””์„œ๋‚˜ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ A๊ฐ€ loadํ•ด์„œ 100์„ ์ฝ๊ณ  increment๊นŒ์ง€ ํ–ˆ๋Š”๋ฐ(101), ๊ทธ ์‚ฌ์ด ์Šค๋ ˆ๋“œ B๊ฐ€ ๋“ค์–ด์™€์„œ ๊ฐ™์€ 100์„ loadํ•ด์„œ incrementยทstore๊นŒ์ง€ ๋๋‚ด๊ณ (101), A๊ฐ€ ๋‹ค์‹œ ๊นจ์–ด๋‚˜์„œ ์ž๊ธฐ ๊ฒฐ๊ณผ 101์„ storeํ•˜๋ฉด, ๋‘ ๋ฒˆ ์ฆ๊ฐ€ํ–ˆ๋Š”๋ฐ๋„ ์ตœ์ข… ๊ฐ’์ด 101์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ 102๊ฐ€ ๋˜์–ด์•ผ ํ–ˆ์ง€๋งŒ 101์ด ๋๊ณ , ์–ด๋–ค ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ์ง€ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค โ€” ๋น„๊ฒฐ์ •์„ฑ(non-determinism) ์ด race์˜ ๋ณธ์งˆ์ž…๋‹ˆ๋‹ค.

Race๋ฅผ ๋ง‰๋Š” ํ•ต์‹ฌ ๊ฐœ๋…์ด Critical Section(์ž„๊ณ„ ์˜์—ญ)์ž…๋‹ˆ๋‹ค. โ€œํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„โ€์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ถ”์ƒ์  ๊ฐœ๋…์ด๊ณ , ๊ทธ ๊ตฌ๊ฐ„์„ ๋ณดํ˜ธํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ๋™๊ธฐํ™” ๊ฐ์ฒด(synchronization primitive) ์ž…๋‹ˆ๋‹ค. ๋™๊ธฐํ™” ๊ฐ์ฒด๋Š” ๋น„์šฉ์— ๋”ฐ๋ผ ์„ธ ์ธต์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ (user-mode first) โ€” Critical Section(Windows)ยทSRWLockยทstd::mutex(MSVC). ๊ฒฝํ•ฉ์ด ์—†์œผ๋ฉด ์‚ฌ์šฉ์ž ๋ชจ๋“œ์—์„œ atomic ๋ช…๋ น์œผ๋กœ ๋๋‚˜๊ณ , ๊ฒฝํ•ฉ ์‹œ์—๋งŒ ์ปค๋„๋กœ ์ง„์ž…ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝํ•ฉ ์—†์„ ๋•Œ ์ˆ˜์‹ญ ns, ๊ฒฝํ•ฉ ์‹œ 1~3 ฮผs.
  • ํ•ญ์ƒ ์ปค๋„ ๊ฐ์ฒด โ€” Mutex(Windows์˜ ์ปค๋„ mutex)ยทSemaphoreยทEvent. ๋งค๋ฒˆ ์‹œ์Šคํ…œ ์ฝœ โ†’ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์˜ ๋ชจ๋“œ ์Šค์œ„์น˜. 1~3 ฮผs. ๋Œ€์‹  ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ (์ด๋ฆ„ ๋ถ€์—ฌ)๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • Lock-free / atomic โ€” std::atomicยทInterlockedExchangeยทCAS(Compare-And-Swap). CPU์˜ LOCK ์ ‘๋‘์‚ฌ ๋ช…๋ น(LOCK CMPXCHG)์œผ๋กœ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ์—†์ด ๋™๊ธฐํ™”. ์ˆ˜ ns. ๋‹จ ์ž๋ฃŒ๊ตฌ์กฐ ์„ค๊ณ„๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ(memory model)์ด ๋˜ ํ•œ ์ธต์˜ ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ํ˜„๋Œ€ CPU๋Š” ๋ช…๋ น ์žฌ๋ฐฐ์น˜(reordering) ์™€ ์บ์‹œ ์ผ๊ด€์„ฑ(cache coherence) ๋•Œ๋ฌธ์—, ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณธ ์“ฐ๊ธฐ ์ˆœ์„œ๊ฐ€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ๋Š” ๋‹ค๋ฅด๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹จ์ˆœํžˆ ๋ฝ๋งŒ ๊ฑธ์–ด์„  ๋ถ€์กฑํ•˜๊ณ , std::atomic์˜ memory_order_acquire/memory_order_release ๊ฐ™์€ memory barrier๋กœ ๋ช…๋ น ์žฌ๋ฐฐ์น˜๋ฅผ ๋ง‰์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. acquire๋Š” โ€œ์ด ์‹œ์  ์ดํ›„์˜ ์ฝ๊ธฐยท์“ฐ๊ธฐ๊ฐ€ ์ด ์‹œ์ ๋ณด๋‹ค ์•ž์œผ๋กœ ์žฌ๋ฐฐ์น˜๋˜์ง€ ์•Š๊ฒŒโ€, release๋Š” โ€œ์ด ์‹œ์  ์ด์ „์˜ ์ฝ๊ธฐยท์“ฐ๊ธฐ๊ฐ€ ์ด ์‹œ์ ๋ณด๋‹ค ๋’ค๋กœ ์žฌ๋ฐฐ์น˜๋˜์ง€ ์•Š๊ฒŒโ€ ๋ง‰๋Š” ํŽœ์Šค(fence)์ž…๋‹ˆ๋‹ค. ๋ฝ ์—†์ด ๋‘ ์Šค๋ ˆ๋“œ ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ๊ณ ๋ฐ›์œผ๋ ค๋ฉด ์ด ๋‘˜์ด ํ•œ ์ง์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Race๋ฅผ ๋ง‰๋Š”๋‹ค๊ณ  ํ•ด์„œ ๋ชจ๋“  ๋ฌธ์ œ๊ฐ€ ํ’€๋ฆฌ๋Š” ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ๋ฝ์„ ์ž˜๋ชป ์“ฐ๋ฉด ์ƒˆ๋กœ์šด ๋ณ‘๋ฆฌ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค โ€” Deadlock(๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ์„œ๋กœ์˜ ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ์˜์›ํžˆ ๋ฉˆ์ถค), Livelock(์„œ๋กœ ์–‘๋ณดํ•˜๋‹ค๊ฐ€ ์ง„ํ–‰์ด ์•ˆ ๋จ), Starvation(ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ์˜์›ํžˆ ๋ฝ์„ ๋ชป ์žก์Œ), Priority Inversion(๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์žก์€ ๋ฝ์„ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š”๋ฐ, ๊ทธ ์‚ฌ์ด์— ์ค‘๊ฐ„ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ CPU๋ฅผ ์ฐจ์ง€ํ•ด ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์ง„ํ–‰ ๋ชป ํ•จ โ€” ํ™”์„ฑ ํƒ์‚ฌ์„  Pathfinder์˜ ์œ ๋ช…ํ•œ ๋ฒ„๊ทธ). Lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋„ ์ž์ฒด ํ•จ์ •์ด ์žˆ์Šต๋‹ˆ๋‹ค โ€” ABA ๋ฌธ์ œ(๊ฐ’์ด Aโ†’Bโ†’A๋กœ ๋ฐ”๋€Œ์—ˆ์ง€๋งŒ CAS๋Š” A๋ฅผ ๋ณด๊ณ  ๋ณ€๊ฒฝ์ด ์—†๋‹ค๊ณ  ์ฐฉ๊ฐ).

์–ธ๋ฆฌ์–ผ ์—”์ง„์€ ์ด ๋ฌธ์ œ๋ฅผ โ€œ๋ฝ์„ ์ค„์ด๋Š”โ€ ๋ฐฉํ–ฅ์ด ์•„๋‹ˆ๋ผ โ€œ๊ณต์œ ๋ฅผ ์ค„์ด๋Š”โ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ’‰๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ GameThreadยทRenderThreadยทRHIThread๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ทธ ์‚ฌ์ด๋ฅผ ๋ช…๋ น ํ(command queue) ์™€ TaskGraph๋กœ ์—ฐ๊ฒฐํ•ด์„œ ํ•œ ์ž์›์„ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๋งŒ์ง€๋Š” ์ƒํ™ฉ ์ž์ฒด๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. UObject๋Š” GameThread๋งŒ ๋งŒ์ง€๊ณ , RenderThread๋Š” GameThread๊ฐ€ ํ•œ ํ”„๋ ˆ์ž„ ๋ถ„๋Ÿ‰์„ ์ •๋ฆฌํ•ด์„œ ๋„˜๊ธด ํ”„๋ก์‹œ ๋ฐ์ดํ„ฐ๋งŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์–ธ๋ฆฌ์–ผ ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ์ฝ”๋“œ๋Š” ๋ฝ์ด ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค โ€” race๊ฐ€ ๋ฐœ์ƒํ•  ์—ฌ์ง€๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ์ฐจ๋‹จํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๋™์‹œ์„ฑ ์—”์ง€๋‹ˆ์–ด๋ง์˜ ๊ฐ€์žฅ ์ข‹์€ ๋‹ต์€ โ€œ๋ฝ์„ ์ž˜ ์“ฐ๋Š” ๊ฒƒโ€์ด ์•„๋‹ˆ๋ผ โ€œ๊ณต์œ ๋ฅผ ์•ˆ ๋งŒ๋“œ๋Š” ๊ฒƒโ€์ž…๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ๊ฐœ๋…

๋ถ„๋ฅ˜ํ‚ค์›Œ๋“œํ•œ ์ค„ ์ •์˜
์ •์˜Race Condition๋‘˜ ์ด์ƒ์˜ ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ ์ ‘๊ทผํ•  ๋•Œ, ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋น„๊ฒฐ์ •์  ํ˜„์ƒ
ย ๋ฐœ์ƒ 4์กฐ๊ฑดโ‘  ๊ณต์œ  ์ž์› โ‘ก ๋‘˜ ์ด์ƒ ๋™์‹œ ์ ‘๊ทผ โ‘ข ์ ์–ด๋„ ํ•˜๋‚˜๋Š” ์“ฐ๊ธฐ โ‘ฃ ์ˆœ์„œ ๋ณด์žฅ ์—†์Œ
ย Critical Section (์ž„๊ณ„ ์˜์—ญ)ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„์˜ ์ถ”์ƒ ๊ฐœ๋…
ย Atomicity (์›์ž์„ฑ)ํ•œ ์—ฐ์‚ฐ์ด ์™ธ๋ถ€์—์„œ ๋ณด๊ธฐ์— โ€œํ•œ ๋ฒˆ์— ๋๋‚˜๊ฑฐ๋‚˜ ์•„์˜ˆ ์•ˆ ์ผ์–ด๋‚œโ€ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ์„ฑ์งˆ
ย ๋น„๊ฒฐ์ •์„ฑ (Non-determinism)๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•ด ์‹คํ–‰๋งˆ๋‹ค ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š” ์„ฑ์งˆ. race์˜ ๋ณธ์งˆ
ย Data RaceC++ ํ‘œ์ค€ ์šฉ์–ด โ€” ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ์— ๋™๊ธฐํ™” ์—†์ด ์ ‘๊ทผ, ์ ์–ด๋„ ํ•˜๋‚˜๊ฐ€ ์“ฐ๊ธฐ ์‹œ UB
ย Race Condition vs Data RaceData race๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ฐจ์› / Race condition์€ ์˜๋ฏธยท๊ฒฐ๊ณผ ์ฐจ์›. ๋ชจ๋“  data race๋Š” race condition์ด์ง€๋งŒ ์—ญ์€ ์•„๋‹˜
๋™๊ธฐํ™” ๊ฐ์ฒดMutex (์ƒํ˜ธ ๋ฐฐ์ œ, MUTual EXclusion)ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ๋ฝ์„ ์žก์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฐํƒ€์  ๋ฝ. Windows ์ปค๋„ ๊ฐ์ฒด๋Š” ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ  ๊ฐ€๋Šฅ
ย Semaphore์นด์šดํ„ฐ ๊ธฐ๋ฐ˜ ๋ฝ โ€” N๊ฐœ ๋™์‹œ ์ ‘๊ทผ ํ—ˆ์šฉ. counting / binary semaphore
ย Critical Section (Windows API)์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„  mutex. ๊ฒฝํ•ฉ ์‹œ์—๋งŒ ์ปค๋„ ์ง„์ž…. ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ๋‚ด ํ•œ์ •
ย SRWLock (Slim Reader/Writer Lock)์‚ฌ์šฉ์ž ๋ชจ๋“œ R/W lock. Vista+. ์ฝ๊ธฐ๋Š” ๋‹ค์ค‘ยท์“ฐ๊ธฐ๋Š” ๋ฐฐํƒ€. std::shared_mutex์˜ ๋‚ด๋ถ€
ย Event (Windows)์‹œ๊ทธ๋„/๋…ผ์‹œ๊ทธ๋„ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด. ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— โ€œ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚ฌ๋‹คโ€ ์•Œ๋ฆผ
ย Condition Variablemutex์™€ ์ง์ง€์–ด ์กฐ๊ฑด ๋งŒ์กฑ ๋Œ€๊ธฐ โ€” wait/notify_one/notify_all
ย Spin Lock๋ฝ ์žกํž ๋•Œ๊นŒ์ง€ busy-wait. ์งง์€ critical section + ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ™˜๊ฒฝ์— ์ ํ•ฉ. ๋‹จ์ผ ์ฝ”์–ด์—์„  ์œ„ํ—˜
ย Recursive Mutex๊ฐ™์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์ž ๊ธˆ ๊ฐ€๋Šฅ. std::recursive_mutex. ์„ค๊ณ„ ๊ฒฐํ•จ ์‹ ํ˜ธ
ย lock_guard / scoped_lock / unique_lockC++ RAII ๋ž˜ํผ. scoped_lock์€ ์—ฌ๋Ÿฌ mutex deadlock-free ์ž ๊ธˆ
Lock-freeLock-free์–ด๋–ค ์Šค๋ ˆ๋“œ๋ผ๋„ finite step ์•ˆ์— ์ง„ํ–‰ ๋ณด์žฅ. CAS ๊ฐ™์€ atomic primitive ๊ธฐ๋ฐ˜
ย Wait-free๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ finite step ์•ˆ์— ์ง„ํ–‰ ๋ณด์žฅ. Lock-free๋ณด๋‹ค ๊ฐ•ํ•œ ๋ณด์žฅ
ย CAS (Compare-And-Swap)โ€œ๊ฐ’์ด expected๋ฉด desired๋กœ ๋ฐ”๊พธ๊ณ  ์„ฑ๊ณต, ์•„๋‹ˆ๋ฉด ์‹คํŒจโ€ ๋‹จ์ผ ๋ช…๋ น. x86 LOCK CMPXCHG
ย atomicC++11 std::atomic<T>. CPU์˜ LOCK ์ ‘๋‘์‚ฌ ๋ช…๋ น์œผ๋กœ read-modify-write๋ฅผ ๋‹จ์ผ ๋ช…๋ น์œผ๋กœ
ย InterlockedExchange / InterlockedIncrementWindows์˜ atomic ํ•จ์ˆ˜๊ตฐ. std::atomic์˜ Windows ๊ตฌํ˜„ ๊ธฐ๋ฐ˜
ย fetch_add / exchange / compare_exchange_weak/strongstd::atomic์˜ ๋ฉค๋ฒ„ ํ•จ์ˆ˜. CAS๋Š” compare_exchange
ย weak vs strong CASweak๋Š” spurious failure ํ—ˆ์šฉ(๋ฃจํ”„ ๊ฐ€์ •), strong์€ ์ง„์งœ ๋น„๊ต ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜
ย ABA ๋ฌธ์ œ๊ฐ’์ด Aโ†’Bโ†’A๋กœ ๋ฐ”๋€Œ์—ˆ์ง€๋งŒ CAS๋Š” A๋ฅผ ๋ณด๊ณ  ๋ณ€๊ฒฝ ์—†๋‹ค๊ณ  ์ฐฉ๊ฐ. ํฌ์ธํ„ฐ lock-free ์ž๋ฃŒ๊ตฌ์กฐ์˜ ํ•จ์ •
ย Tagged Pointer / Hazard Pointer / Epoch-based reclamationABA ํšŒํ”ผ ๊ธฐ๋ฒ• โ€” ๋ฒ„์ „ ์นด์šดํ„ฐ / ์œ„ํ—˜ ํฌ์ธํ„ฐ ๋“ฑ๋ก / ์—ํญ ๊ธฐ๋ฐ˜ ํšŒ์ˆ˜
Memory ModelMemory Model (๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ)CPUยท์–ธ์–ด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์˜ ์ˆœ์„œยท๊ฐ€์‹œ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๊ทœ์น™
ย Sequential Consistency (SC, ์ˆœ์ฐจ ์ผ๊ด€์„ฑ)๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ์—ฐ์‚ฐ์„ ๋ณธ๋‹ค๋Š” ๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ชจ๋ธ
ย Memory Reordering (๋ฉ”๋ชจ๋ฆฌ ์žฌ๋ฐฐ์น˜)์ปดํŒŒ์ผ๋ŸฌยทCPU๊ฐ€ ์„ฑ๋Šฅ์„ ์œ„ํ•ด ๋ช…๋ น ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ. ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์˜๋ฏธ๋Š” ๋ณด์กดํ•˜์ง€๋งŒ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ์—์„  race ์›์ธ
ย Memory Barrier / Fence์žฌ๋ฐฐ์น˜๋ฅผ ๋ง‰๋Š” ํŽœ์Šค ๋ช…๋ น. acquire / release / full barrier
ย memory_order_relaxed์ˆœ์„œ ๋ณด์žฅ ์—†์Œ. counter์ฒ˜๋Ÿผ ์ˆœ์„œ ๋ฌด๊ด€ํ•  ๋•Œ๋งŒ
ย memory_order_acquire์ด ์‹œ์  ์ดํ›„์˜ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์ด ์ด ์‹œ์ ๋ณด๋‹ค ์•ž์œผ๋กœ ์žฌ๋ฐฐ์น˜๋˜์ง€ ์•Š์Œ. load์— ๋ถ™์Œ
ย memory_order_release์ด ์‹œ์  ์ด์ „์˜ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์ด ์ด ์‹œ์ ๋ณด๋‹ค ๋’ค๋กœ ์žฌ๋ฐฐ์น˜๋˜์ง€ ์•Š์Œ. store์— ๋ถ™์Œ
ย memory_order_seq_cst๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ชจ๋ธ. atomic์˜ ๊ธฐ๋ณธ๊ฐ’. ๋น„์šฉ ํผ
ย acquire-release ํŽ˜์–ดrelease store โ†’ acquire load๋กœ ๋‘ ์Šค๋ ˆ๋“œ ๊ฐ„ happens-before ๊ด€๊ณ„ ์„ฑ๋ฆฝ
ย happens-beforeA์˜ ๊ฒฐ๊ณผ๊ฐ€ B์—์„œ ๋ณด์ด๋„๋ก ๋ณด์žฅ๋˜๋Š” ์ˆœ์„œ ๊ด€๊ณ„
ย x86 ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ (TSO, Total Store Order)๋น„๊ต์  ๊ฐ•ํ•œ ๋ชจ๋ธ. storeโ†’load๋งŒ ์žฌ๋ฐฐ์น˜ ํ—ˆ์šฉ. ๊ทธ๋ž˜์„œ x86์—์„  memory barrier ๋น„์šฉ์ด ๋น„๊ต์  ์ž‘์Œ
ย ARM/RISC-V (Weak Memory Model)๊ฑฐ์˜ ๋ชจ๋“  ์žฌ๋ฐฐ์น˜ ํ—ˆ์šฉ. ๋ช…์‹œ์  barrier ํ•„์ˆ˜
๋ณ‘๋ฆฌDeadlock (๊ต์ฐฉ ์ƒํƒœ)๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ์„œ๋กœ์˜ ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ์˜์›ํžˆ ๋ฉˆ์ถค. 4์กฐ๊ฑด: ์ƒํ˜ธ๋ฐฐ์ œยท์ ์œ ์™€ ๋Œ€๊ธฐยท๋น„์„ ์ ยท์ˆœํ™˜ ๋Œ€๊ธฐ
ย Deadlock ํšŒํ”ผ๋ฝ ์ˆœ์„œ ๊ณ ์ •(lock ordering), std::scoped_lock(์—ฌ๋Ÿฌ ๋ฝ ๋™์‹œ ํš๋“), ํƒ€์ž„์•„์›ƒ
ย Livelock๋ฝ์€ ์•ˆ ์žก๊ณ  ์žˆ์ง€๋งŒ ์„œ๋กœ ์–‘๋ณดํ•˜๋‹ค๊ฐ€ ์ง„ํ–‰ ์•ˆ ๋จ. retry loop์˜ ํ”ํ•œ ๋ฒ„๊ทธ
ย Starvation (๊ธฐ์•„)ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ์˜์›ํžˆ ๋ฝ์„ ๋ชป ์žก์Œ. ์šฐ์„ ์ˆœ์œ„ ์ •์ฑ…ยทSRWLock์˜ writer starvation
ย Priority Inversion (์šฐ์„ ์ˆœ์œ„ ์—ญ์ „)๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์žก์€ ๋ฝ์„ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š”๋ฐ, ์ค‘๊ฐ„ ์šฐ์„ ์ˆœ์œ„๊ฐ€ CPU ์ฐจ์ง€
ย Priority Inheritance์šฐ์„ ์ˆœ์œ„ ์—ญ์ „ ํ•ด๋ฒ• โ€” ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ ์žก์€ ๋™์•ˆ ์ผ์‹œ์ ์œผ๋กœ ์šฐ์„ ์ˆœ์œ„ ์ƒ์Šน
Windows APICRITICAL_SECTIONInitializeCriticalSection/EnterCriticalSection/LeaveCriticalSection. ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ํ•œ์ •, ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ 
ย SRWLOCKAcquireSRWLockShared/AcquireSRWLockExclusive. Reader/Writer ๋ถ„๋ฆฌ. ๊ฐ€๋ฒผ์›€
ย Mutex (์ปค๋„)CreateMutex/WaitForSingleObject/ReleaseMutex. ์ด๋ฆ„ ๋ถ€์—ฌ ์‹œ ํ”„๋กœ์„ธ์Šค ๊ฐ„
ย SemaphoreCreateSemaphore/WaitForSingleObject/ReleaseSemaphore. ์นด์šดํŒ… ๋ฝ
ย EventCreateEvent/SetEvent/ResetEvent/PulseEvent
ย CONDITION_VARIABLESleepConditionVariableSRW/WakeConditionVariable
ย InterlockedExchange ๊ณ„์—ดx86 LOCK ์ ‘๋‘์‚ฌ ๋ช…๋ น ๋ž˜ํผ. atomic์˜ Windows ๊ตฌํ˜„
ย MemoryBarrier() / _mm_mfence๋ช…์‹œ์  ๋ฉ”๋ชจ๋ฆฌ ํŽœ์Šค
POSIX APIpthread_mutex_tPOSIX mutex. ๊ธฐ๋ณธยทrecursiveยทerrorcheckยทrobust ์ข…๋ฅ˜
ย pthread_rwlock_tReader/Writer lock
ย pthread_cond_tCondition variable. pthread_cond_wait/signal/broadcast
ย sem_t (POSIX semaphore)Named (ํ”„๋กœ์„ธ์Šค ๊ฐ„) / unnamed (์Šค๋ ˆ๋“œ ๊ฐ„)
ย __atomic_* / GCC builtinsC11 _AtomicยทC++11 std::atomic ์ด์ „์˜ GCC ๋นŒํŠธ์ธ
ย PTHREAD_PROCESS_SHARED ์†์„ฑmutex/cond๋ฅผ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์— ๋†“๊ณ  ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ 
๋น„์šฉatomic CAS์ˆ˜ ns. ๋ฝ ์—†์ด ๋™๊ธฐํ™”
ย ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฝ (๊ฒฝํ•ฉ ์—†์Œ)์ˆ˜์‹ญ ns. Critical SectionยทSRWLock ๋ฝ ํš๋“๋งŒ
ย ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฝ (๊ฒฝํ•ฉ)1~3 ฮผs. ์ปค๋„ ์ง„์ž… + ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ
ย ์ปค๋„ ๊ฐ์ฒด ๋ฝ1~3 ฮผs. ํ•ญ์ƒ ์‹œ์Šคํ…œ ์ฝœ
์–ธ๋ฆฌ์–ผGameThreadUObjectยทAActorยทTick ์ฒ˜๋ฆฌ. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ โ€” ๋ฝ ๊ฑฐ์˜ ์—†์Œ (๋‹จ์ผ ์Šค๋ ˆ๋“œ ๊ฐ€์ •)
ย RenderThread๋ Œ๋”๋ง ๋ช…๋ น ์ฒ˜๋ฆฌ. GameThread์™€ ๋ถ„๋ฆฌ๋˜์–ด race ์ฐจ๋‹จ
ย TaskGraph์˜์กด์„ฑ ๊ธฐ๋ฐ˜ ์ž‘์—… ๋ถ„ํ• . ๋ฝ ๋Œ€์‹  ์ž‘์—… ๊ทธ๋ž˜ํ”„๋กœ ๋™์‹œ์„ฑ ํ‘œํ˜„
ย FCriticalSection์–ธ๋ฆฌ์–ผ์˜ Critical Section ๋ž˜ํผ
ย FScopeLockRAII ๋ฝ ๊ฐ€๋“œ (std::lock_guard์™€ ๋™๋“ฑ)
ย FThreadSafeCounteratomic counter (std::atomic<int32> ๋™๋“ฑ)
ย TQueue<T, EQueueMode::Spsc/Mpsc>lock-free ํ โ€” single/multi producer single consumer
ย ENQUEUE_RENDER_COMMANDGameThread โ†’ RenderThread ๋žŒ๋‹ค ์ „๋‹ฌ ๋งคํฌ๋กœ. ๋ฝ ์—†์ด ๋ช…๋ น ํ๋กœ

๋ชฉ์ฐจ

  1. ํ•ต์‹ฌ ์š”์•ฝ ์นด๋“œ
  2. ํ•œ ์ค„ ์ •์˜ โ€” Race Condition์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€
  3. ๋ฐœ์ƒ ์กฐ๊ฑด 4๊ฐ€์ง€์™€ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ˆ์ œ
  4. Critical Section โ€” ์ž„๊ณ„ ์˜์—ญ์˜ ๊ฐœ๋…
  5. ๋™๊ธฐํ™” ๊ฐ์ฒด ์นดํƒˆ๋กœ๊ทธ โ€” MutexยทSemaphoreยทCritical SectionยทSRWLockยทEventยทCondition Variable
  6. Lock-freeยทatomicยทCAS โ€” ๋ฝ ์—†๋Š” ๋™๊ธฐํ™”
  7. Memory OrderingยทMemory Barrier โ€” acquire/release ํŽ˜์–ด
  8. ABA ๋ฌธ์ œ โ€” Lock-free์˜ ํ•จ์ •
  9. Deadlock โ€” ๋ฐœ์ƒ 4์กฐ๊ฑด๊ณผ ํšŒํ”ผ ์ „๋žต
  10. LivelockยทStarvation โ€” Deadlock์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ •์ฒด
  11. Priority Inversion โ€” Pathfinder ํ™”์„ฑ ํƒ์‚ฌ์„  ์‚ฌ๋ก€
  12. Windows API vs POSIX API ๋น„๊ต
  13. ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ ์ •๋ฆฌ โ€” ์–ด๋А ๋™๊ธฐํ™”๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋น„์‹ผ๊ฐ€
  14. ์–ธ๋ฆฌ์–ผ์—์„œ์˜ Race Condition ํšŒํ”ผ โ€” ์Šค๋ ˆ๋“œ ๋ถ„๋ฆฌยทTaskGraph
  15. ๊ผฌ๋ฆฌ์งˆ๋ฌธ ์˜ˆ์ƒ ๊ฒฝ๋กœ
  16. ํ•ต์‹ฌ ์š”์•ฝ ์นด๋“œ (์žฌ๊ฒŒ์žฌ)
  17. ํšŒ๊ท€ ๋‹ค๋ฆฌ โ€” ๋‹ค๋ฅธ CS ํŒŒ์ผ ์—ฐ๊ฒฐ

1. ํ•ต์‹ฌ ์š”์•ฝ ์นด๋“œ

30์ดˆ ๋‹ต๋ณ€

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Race Condition = ๋‘˜ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ ์ ‘๊ทผ,
                  ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋น„๊ฒฐ์ •์  ํ˜„์ƒ.

๋ฐœ์ƒ 4์กฐ๊ฑด:
  โ‘  ๊ณต์œ  ์ž์› ์กด์žฌ
  โ‘ก ๋‘˜ ์ด์ƒ์ด ๋™์‹œ ์ ‘๊ทผ
  โ‘ข ์ ์–ด๋„ ํ•˜๋‚˜๋Š” ์“ฐ๊ธฐ (write)
  โ‘ฃ ์ˆœ์„œ๋ฅผ OS๊ฐ€ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ

๋Œ€ํ‘œ ์˜ˆ: count++ ๊ฐ€ load โ†’ inc โ†’ store 3๋‹จ๊ณ„ โ†’ ๊ทธ ์‚ฌ์ด ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ
        โ†’ ๊ฒฐ๊ณผ ๋น„๊ฒฐ์ • (102 ๊ธฐ๋Œ€, 101 ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Œ)

ํ•ด๊ฒฐ โ€” Critical Section์„ ๋™๊ธฐํ™” ๊ฐ์ฒด๋กœ ๋ณดํ˜ธ:
  โ‘  ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„  (์ˆ˜์‹ญ ns ~ 1 ฮผs)
     - Critical Section (Win) / std::mutex / SRWLock
  โ‘ก ์ปค๋„ ๊ฐ์ฒด (1~3 ฮผs, ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ  ๊ฐ€๋Šฅ)
     - Mutex / Semaphore / Event
  โ‘ข Lock-free / atomic / CAS (์ˆ˜ ns)
     - std::atomic<T>, InterlockedExchange
     - ์ž๋ฃŒ๊ตฌ์กฐ ์„ค๊ณ„ ๋งค์šฐ ์–ด๋ ต๋‹ค

Memory Model:
  CPUยท์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ช…๋ น ์žฌ๋ฐฐ์น˜ โ†’ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ๊ฐ€์‹œ์„ฑ ๊นจ์ง
  โ†’ memory_order_acquire / release ํŽ˜์–ด๋กœ fence
  โ†’ x86์€ TSO (๊ฐ•ํ•จ) / ARM์€ weak (barrier ํ•„์ˆ˜)

๋ณ‘๋ฆฌ:
  Deadlock      = ์„œ๋กœ์˜ ๋ฝ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ฉˆ์ถค (4์กฐ๊ฑด: ์ƒํ˜ธ๋ฐฐ์ œยท์ ์œ ๋Œ€๊ธฐยท๋น„์„ ์ ยท์ˆœํ™˜)
  Livelock      = ๋ฝ ์•ˆ ์žก๊ณ  ์–‘๋ณด๋งŒ ํ•˜๋‹ค ์ง„ํ–‰ ์•ˆ ๋จ
  Starvation    = ํŠน์ • ์Šค๋ ˆ๋“œ ์˜์›ํžˆ ๋ฝ ๋ชป ์žก์Œ
  Priority Inv. = ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ๋ฝ์„ ๋†’์€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๊ธฐ๋‹ค๋ฆผ (Pathfinder ์‚ฌ๋ก€)
  ABA           = lock-free์—์„œ Aโ†’Bโ†’A ๋ชป ์•Œ์•„์ฑ”

์–ธ๋ฆฌ์–ผ ์ฒ ํ•™ โ€” "๋ฝ์„ ์ž˜ ์“ฐ์ง€ ๋ง๊ณ , ๊ณต์œ ๋ฅผ ์•ˆ ๋งŒ๋“ ๋‹ค":
  GameThread (UObject) / RenderThread (proxy) / RHIThread (GPU ๋ช…๋ น) ๋ถ„๋ฆฌ
  TaskGraph๋กœ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„ ํ‘œํ˜„ โ€” ๋ฝ ๋Œ€์‹  ์ž‘์—… ์ˆœ์„œ
  ENQUEUE_RENDER_COMMAND โ€” ๋ช…๋ น ํ๋กœ GameThread โ†’ RenderThread

๊ผฌ๋ฆฌ์งˆ๋ฌธ ์—ฐ๊ฒฐ ๋งต

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Race Condition
โ”œโ”€โ”€ ๋ฐœ์ƒ ์กฐ๊ฑด (์™œ ์ผ์–ด๋‚˜๋‚˜?)
โ”‚   โ”œโ”€โ”€ ๊ณต์œ  ์ž์› (ํž™ยท์ „์—ญยท์ •์ )
โ”‚   โ”œโ”€โ”€ ๋™์‹œ ์ ‘๊ทผ (๋‘˜ ์ด์ƒ ์Šค๋ ˆ๋“œ)
โ”‚   โ”œโ”€โ”€ ์“ฐ๊ธฐ ํฌํ•จ
โ”‚   โ””โ”€โ”€ ๋น„๊ฒฐ์ •์  ์Šค์ผ€์ค„๋ง โ€” ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21) ํšŒ๊ท€
โ”œโ”€โ”€ Critical Section (ํ•ด๊ฒฐ ์ถ”์ƒ)
โ”‚   โ””โ”€โ”€ ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰
โ”œโ”€โ”€ ๋™๊ธฐํ™” ๊ฐ์ฒด (๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ)
โ”‚   โ”œโ”€โ”€ Critical Section (Win, ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค)
โ”‚   โ”œโ”€โ”€ SRWLock (R/W ๋ถ„๋ฆฌ)
โ”‚   โ”œโ”€โ”€ std::mutex (์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ )
โ”‚   โ”œโ”€โ”€ Mutex ์ปค๋„ ๊ฐ์ฒด (ํ”„๋กœ์„ธ์Šค ๊ฐ„, IPC(22) ํšŒ๊ท€)
โ”‚   โ”œโ”€โ”€ Semaphore (์นด์šดํŒ…)
โ”‚   โ”œโ”€โ”€ Event (์•Œ๋ฆผ)
โ”‚   โ””โ”€โ”€ Condition Variable (์กฐ๊ฑด ๋Œ€๊ธฐ)
โ”œโ”€โ”€ Lock-free
โ”‚   โ”œโ”€โ”€ atomic (std::atomic, InterlockedExchange)
โ”‚   โ”œโ”€โ”€ CAS (Compare-And-Swap)
โ”‚   โ”œโ”€โ”€ ABA ๋ฌธ์ œ + ํšŒํ”ผ (tagged pointer)
โ”‚   โ””โ”€โ”€ lock-free vs wait-free
โ”œโ”€โ”€ Memory Model
โ”‚   โ”œโ”€โ”€ memory_order_relaxed / acquire / release / seq_cst
โ”‚   โ”œโ”€โ”€ Memory Barrier / Fence
โ”‚   โ”œโ”€โ”€ happens-before
โ”‚   โ”œโ”€โ”€ x86 TSO vs ARM weak
โ”‚   โ””โ”€โ”€ ์ปดํŒŒ์ผ๋Ÿฌ vs CPU ์žฌ๋ฐฐ์น˜
โ”œโ”€โ”€ ๋ณ‘๋ฆฌ
โ”‚   โ”œโ”€โ”€ Deadlock (4์กฐ๊ฑดยทํšŒํ”ผยทscoped_lock)
โ”‚   โ”œโ”€โ”€ Livelock
โ”‚   โ”œโ”€โ”€ Starvation
โ”‚   โ””โ”€โ”€ Priority Inversion (Pathfinder, Priority Inheritance)
โ”œโ”€โ”€ Windows API
โ”‚   โ”œโ”€โ”€ CRITICAL_SECTION
โ”‚   โ”œโ”€โ”€ SRWLOCK
โ”‚   โ”œโ”€โ”€ Mutex (์ปค๋„)
โ”‚   โ”œโ”€โ”€ Semaphore / Event
โ”‚   โ”œโ”€โ”€ CONDITION_VARIABLE
โ”‚   โ””โ”€โ”€ InterlockedExchange ๊ณ„์—ด
โ”œโ”€โ”€ POSIX API
โ”‚   โ”œโ”€โ”€ pthread_mutex_t (+ recursive / errorcheck / robust)
โ”‚   โ”œโ”€โ”€ pthread_rwlock_t
โ”‚   โ”œโ”€โ”€ pthread_cond_t
โ”‚   โ””โ”€โ”€ sem_t (named / unnamed)
โ””โ”€โ”€ ์–ธ๋ฆฌ์–ผ
    โ”œโ”€โ”€ GameThread / RenderThread / RHIThread ๋ถ„๋ฆฌ
    โ”œโ”€โ”€ TaskGraph
    โ”œโ”€โ”€ FCriticalSection / FScopeLock
    โ”œโ”€โ”€ FThreadSafeCounter (atomic)
    โ”œโ”€โ”€ TQueue (lock-free)
    โ””โ”€โ”€ ENQUEUE_RENDER_COMMAND

2. ํ•œ ์ค„ ์ •์˜ โ€” Race Condition์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€

ํ•ต์‹ฌ ํ•œ ๋ฌธ์žฅ

Race Condition์€ ๋‘˜ ์ด์ƒ์˜ ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ ์ ‘๊ทผํ•  ๋•Œ, ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋น„๊ฒฐ์ •์  ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

๋‹จ์–ด ๋ถ„ํ•ด

Race๋Š” โ€œ๊ฒฝ์ฃผโ€๋ผ๋Š” ๋œป ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ž์›์— ๋Œ€ํ•ด ๊ฒฝ์ฃผํ•˜๋Š”๋ฐ, ๋ˆ„๊ฐ€ ๋จผ์ € ๋„์ฐฉํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. Condition์€ โ€œ์กฐ๊ฑดโ€ ๋˜๋Š” โ€œ์ƒํƒœโ€ โ€” ๊ทธ ๊ฒฝ์ฃผ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ์‹œ์Šคํ…œ์ด ์ž˜๋ชป๋œ ์ƒํƒœ์— ๋น ์งˆ ์ˆ˜ ์žˆ๋Š” ์กฐ๊ฑด์ด ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

Race Condition vs Data Race

C++ ํ‘œ์ค€์€ ๋‘˜์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค:

์šฉ์–ด์ •์˜๋ฒ”์œ„
Data Race๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์œ„์น˜์— ๋™๊ธฐํ™” ์—†์ด ์ ‘๊ทผ, ์ ์–ด๋„ ํ•˜๋‚˜๊ฐ€ ์“ฐ๊ธฐ. C++์—์„  UB๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ˆ˜์ค€
Race Condition์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋ชจ๋“  ์ƒํ™ฉ์˜๋ฏธยท๊ฒฐ๊ณผ ์ˆ˜์ค€

๋ชจ๋“  data race๋Š” race condition์ด์ง€๋งŒ, race condition์ด ๋ชจ๋‘ data race๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‘ atomic ์—ฐ์‚ฐ์„ ์ ์ ˆํ•œ ์ˆœ์„œ ์—†์ด ํ˜ธ์ถœํ•˜๋ฉด data race๋Š” ์—†์ง€๋งŒ(atomic์ด๋ผ UB ์•„๋‹˜) ๊ฒฐ๊ณผ๋Š” ๋น„๊ฒฐ์ •์ ์ด๋ผ race condition์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
std::atomic<int> count{0};
// ์Šค๋ ˆ๋“œ A
if (count.load() == 0) {        // 1) ์ฝ์Œ
    count.store(1);              // 2) ์“ฐ๊ธฐ - ์ด ์‚ฌ์ด์— B๊ฐ€ ๋“ค์–ด์˜ค๋ฉด race condition
}
// ์Šค๋ ˆ๋“œ B๋„ ๋™์‹œ์— ๊ฐ™์€ ์ฝ”๋“œ ์‹คํ–‰
// โ†’ ๋‘˜ ๋‹ค 1์„ ์”€. ๋‘ ๋ฒˆ ์ดˆ๊ธฐํ™” ๊ฐ™์€ ์˜๋ฏธ์  race condition.
// โ†’ data race๋Š” ์—†์Œ (atomic์ด๋ผ).

๋ฉด์ ‘์—์„œ๋Š” ๋ณดํ†ต ๋‘˜์„ ํ†ตํ‹€์–ด โ€œrace conditionโ€์ด๋ผ ๋ถ€๋ฅด์ง€๋งŒ, ๋‘ ๋‹จ์–ด๋ฅผ ๊ตฌ๋ถ„ํ•ด ๋‹ตํ•˜๋ฉด ๊นŠ์ด๊ฐ€ ์‚ฐ๋‹ค.

ํ๋ฆ„ ํ•œ๋ˆˆ์—

1
2
3
4
5
6
7
8
9
10
์Šค๋ ˆ๋“œ A                      ๊ณต์œ  ์ž์› count            ์Šค๋ ˆ๋“œ B
โ”€โ”€โ”€โ”€โ”€                         โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                  โ”€โ”€โ”€โ”€โ”€
load count โ†’ 100               [100]
                                                          load count โ†’ 100
increment โ†’ 101
                                                          increment โ†’ 101
                                                          store 101
                               [101]
store 101
                               [101]   โ† ๋‘ ๋ฒˆ +1 ํ–ˆ๋Š”๋ฐ +1๋งŒ ๋ฐ˜์˜

์ด๊ฒŒ race condition์˜ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ˆœ์„œ๋ฅผ ํ™”์‚ดํ‘œ๋กœ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒฝ์šฐ ์ค‘์— ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜๋ฉด ๊ทธ๊ฒƒ์ด race condition์ž…๋‹ˆ๋‹ค.


3. ๋ฐœ์ƒ ์กฐ๊ฑด 4๊ฐ€์ง€์™€ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ˆ์ œ

3.1 ๋ฐœ์ƒ 4์กฐ๊ฑด

์กฐ๊ฑด์˜๋ฏธ์œ„๋ฐ˜ ์‹œ
โ‘  ๊ณต์œ  ์ž์›๋‘˜ ์ด์ƒ์ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌยทํŒŒ์ผยท์†Œ์ผ“ยท๋””๋ฐ”์ด์Šค์ž์›์ด ์ง„์งœ thread-local์ด๋ฉด race ์—†์Œ
โ‘ก ๋‘˜ ์ด์ƒ ๋™์‹œ ์ ‘๊ทผ๊ฐ™์€ ์‹œ์ ์— ๋‘ ์Šค๋ ˆ๋“œ ์ด์ƒ์ด ์ ‘๊ทผ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•˜๋ฉด race ์—†์Œ
โ‘ข ์ ์–ด๋„ ํ•˜๋‚˜๋Š” ์“ฐ๊ธฐ๋ชจ๋‘ ์ฝ๊ธฐ๋งŒ ํ•˜๋ฉด race ์—†์Œconst ๋ฐ์ดํ„ฐ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ์ฝ๊ธฐ๋Š” ์•ˆ์ „
โ‘ฃ ์ˆœ์„œ ๋ณด์žฅ ์—†์ŒOSยทCPU๊ฐ€ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ๋™๊ธฐํ™” ๊ฐ์ฒด๋กœ ์ˆœ์„œ ๊ฐ•์ œํ•˜๋ฉด race ์—†์Œ

์ด ๋„ค ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋งŒ ๊นจ๋„ race๋Š” ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. race๋ฅผ ๋ง‰๋Š” ๋ชจ๋“  ๊ธฐ๋ฒ•์€ ์ด ์ค‘ ํ•˜๋‚˜๋ฅผ ๊นจ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1
2
3
4
์กฐ๊ฑด โ‘  ๊นจ๊ธฐ: ์ž์›์„ thread-local๋กœ                  โ†’ thread_local ๋ณ€์ˆ˜
์กฐ๊ฑด โ‘ก ๊นจ๊ธฐ: ๋‹จ์ผ ์Šค๋ ˆ๋“œ ๋ชจ๋ธ                       โ†’ ์–ธ๋ฆฌ์–ผ GameThread ๋‹จ์ผํ™”
์กฐ๊ฑด โ‘ข ๊นจ๊ธฐ: immutable (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€)                  โ†’ const, std::shared_ptr<const T>
์กฐ๊ฑด โ‘ฃ ๊นจ๊ธฐ: ๋™๊ธฐํ™” ๊ฐ์ฒด๋กœ ์ˆœ์„œ ๊ฐ•์ œ                โ†’ mutex, atomic

์–ธ๋ฆฌ์–ผ์ด GameThread/RenderThread๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฑด โ‘ก๋ฅผ ๊นจ๋Š” ์ „๋žต์ž…๋‹ˆ๋‹ค. const ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฑด โ‘ข์„ ๊นจ๋Š” ์ „๋žต์ž…๋‹ˆ๋‹ค. mutex๋กœ ๋ฝ ๊ฑฐ๋Š” ๊ฑด โ‘ฃ๋ฅผ ๊นจ๋Š” ์ „๋žต์ž…๋‹ˆ๋‹ค. ์–ด๋А ์ชฝ์ด ๊ฐ€์žฅ ์ข‹์€์ง€๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด์ง€๋งŒ, ์ˆœ์„œ๋Œ€๋กœ โ‘  > โ‘ก > โ‘ข > โ‘ฃ ๊ฐ€ ๋น„์šฉ์ด ๋‚ฎ์€ ์ˆœ์ž…๋‹ˆ๋‹ค.

3.2 ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ˆ์ œ โ€” counter ์ฆ๊ฐ€

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int g_count = 0;

void worker() {
    for (int i = 0; i < 100000; ++i) {
        g_count++;     // race condition!
    }
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);
    t1.join(); t2.join();
    std::cout << g_count << std::endl;
    // ์˜ˆ์ƒ: 200000
    // ์‹ค์ œ: 100023 / 187432 / ... ๋งค ์‹คํ–‰๋งˆ๋‹ค ๋‹ค๋ฆ„
}

์™œ ๊ทธ๋Ÿฐ๊ฐ€ โ€” g_count++๊ฐ€ ๋‹จ์ผ ๋ช…๋ น์ด ์•„๋‹ˆ๋‹ค:

1
2
3
4
5
6
7
8
g_count++ ๋ฅผ x86 ์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ๋ณด๋ฉด:

   mov eax, [g_count]       ; โ‘  load: ๋ฉ”๋ชจ๋ฆฌ โ†’ ๋ ˆ์ง€์Šคํ„ฐ
   inc eax                  ; โ‘ก increment: ๋ ˆ์ง€์Šคํ„ฐ ์•ˆ์—์„œ +1
   mov [g_count], eax       ; โ‘ข store: ๋ ˆ์ง€์Šคํ„ฐ โ†’ ๋ฉ”๋ชจ๋ฆฌ

์„ธ ๋ช…๋ น ์‚ฌ์ด ์–ด๋””์„œ๋‚˜ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค.
ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋Š” ๋ช…๋ น ๋‹จ์œ„๋กœ๋งŒ ๋ผ์–ด๋“ ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
์Šค๋ ˆ๋“œ A์˜ ์‹œ์                g_count                   ์Šค๋ ˆ๋“œ B์˜ ์‹œ์ 
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                     โ”€โ”€โ”€โ”€โ”€โ”€                    โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
mov eax, [g_count]            [100]
eax = 100
inc eax (=101)
                              [100]                     mov eax, [g_count]
                                                        eax = 100
                                                        inc eax (=101)
                                                        mov [g_count], eax
                              [101]
mov [g_count], eax
                              [101]   โ† ๋‘ ๋ฒˆ ++ ํ–ˆ๋Š”๋ฐ ํ•œ ๋ฒˆ๋งŒ ๋ฐ˜์˜

3.3 ATOMICํ•˜์ง€ ์•Š์€ ๋” ํฐ ๋‹จ์œ„

๊ฐ™์€ ํŒจํ„ด์ด ์ด๋ฆ„ํ‘œ๋ฅผ ๋ฐ”๊ฟ”์„œ ์–ด๋””๋“  ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
// ์€ํ–‰ ๊ณ„์ขŒ ์ด์ฒด - "์›์ž์„ฑ" ๊นจ์ง
void Transfer(Account& from, Account& to, int amount) {
    from.balance -= amount;     // โ‘  ์ถœ๊ธˆ
    // โ†‘ ์—ฌ๊ธฐ์„œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜ โ†’ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ from ๋ณด๋ฉด ์ž”์•ก ๋ถ€์กฑ
    // โ†‘ ๋˜๋Š” from์—์„œ ๋น ์ง„ ๋ˆ์ด ์–ด๋””์—๋„ ์—†๋Š” ์ƒํƒœ
    to.balance   += amount;     // โ‘ก ์ž…๊ธˆ
}
1
2
3
4
5
6
7
// linked list ๋…ธ๋“œ ์‚ฝ์ž… - ์ผ๊ด€์„ฑ ๊นจ์ง
void Insert(Node* newNode) {
    newNode->next = head;       // โ‘  newNodeโ†’๋‹ค์Œ
    // โ†‘ ์—ฌ๊ธฐ์„œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ head ๋ณด๋ฉด newNode๊ฐ€ ์•ˆ ๋ณด์ž„
    // โ†‘ ๊ทธ ์Šค๋ ˆ๋“œ๊ฐ€ head๋ฅผ ๋‹ค๋ฅธ ๋…ธ๋“œ๋กœ ๋ฐ”๊ฟ”๋ฒ„๋ฆฌ๋ฉด newNode๊ฐ€ ์‚ฌ๋ผ์ง
    head = newNode;             // โ‘ก headโ†’newNode
}
1
2
3
4
5
6
// ๊ฒŒ์ž„ ์ธ๋ฒคํ† ๋ฆฌ ์ถ”๊ฐ€
void AddItem(Item* item) {
    items[count] = item;        // โ‘  ์Šฌ๋กฏ์— ์ €์žฅ
    // โ†‘ ์—ฌ๊ธฐ์„œ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ count๋ฅผ ๋ด์„œ ๊ฐ™์€ ์Šฌ๋กฏ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์Œ
    count++;                    // โ‘ก ์นด์šดํ„ฐ ์ฆ๊ฐ€
}

์„ธ ์˜ˆ์ œ ๋ชจ๋‘ ๋ณธ์งˆ์€ ๊ฐ™์Šต๋‹ˆ๋‹ค โ€” ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์—ฐ์‚ฐ์ด ํ•˜๋‚˜์˜ ๋‹จ์œ„๋กœ ๋ณดํ˜ธ๋ฐ›์ง€ ๋ชปํ•ด ์ค‘๊ฐ„ ์ƒํƒœ๊ฐ€ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

3.4 ๋‹จ์ผ ๋ช…๋ น์ด๋ผ๋„ race๊ฐ€ ๊ฐ€๋Šฅ

64๋น„ํŠธ ๊ฐ’์„ 32๋น„ํŠธ ๋จธ์‹ ์—์„œ ์ฝ๊ณ  ์“ฐ๋ฉด ๋‘ ๋ฒˆ์— ๋‚˜๋ˆ  ์ผ์–ด๋‚˜์„œ torn read/write๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค:

1
2
3
4
5
6
7
// 32๋น„ํŠธ ๋จธ์‹ ์—์„œ
int64_t g_value = 0;
// ์Šค๋ ˆ๋“œ A: g_value = 0x1234567890ABCDEF
//   โ†’ mov [g_value+0], 0x90ABCDEF
//   โ†‘ ์—ฌ๊ธฐ์„œ ์Šค์œ„์น˜
//   โ†’ mov [g_value+4], 0x12345678
// ์Šค๋ ˆ๋“œ B: ์œ„ ์‚ฌ์ด์— ์ฝ์œผ๋ฉด 0x0000000090ABCDEF (์ฐข์–ด์ง„ ๊ฐ’)

64๋น„ํŠธ ๋จธ์‹ ์—์„œ 64๋น„ํŠธ ์ •๋ ฌ๋œ ๊ฐ’์€ ๋‹จ์ผ ๋ช…๋ น์ด์ง€๋งŒ, ๊ทธ๊ฒƒ๋„ ํ‘œ์ค€์ด ๋ณด์žฅํ•˜์ง„ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ std::atomic<int64_t>๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์จ์•ผ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.


4. Critical Section โ€” ์ž„๊ณ„ ์˜์—ญ์˜ ๊ฐœ๋…

4.1 ์ •์˜

Critical Section(์ž„๊ณ„ ์˜์—ญ) ์€ โ€œํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„โ€ ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ถ”์ƒ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. ์ž์›์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ ๊ตฌ๊ฐ„์˜ ๊ฐœ๋…์ด๋ผ๋Š” ๊ฒŒ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
std::mutex mtx;
int g_count = 0;

void worker() {
    for (int i = 0; i < 100000; ++i) {
        mtx.lock();              // โ”€โ”
        g_count++;               //  โ”‚ โ† Critical Section
        mtx.unlock();            // โ”€โ”˜
    }
}

mtx.lock()๊ณผ mtx.unlock() ์‚ฌ์ด๊ฐ€ critical section์ด๊ณ , ์ด ๊ตฌ๊ฐ„ ์•ˆ์˜ ๋ชจ๋“  ์ฝ”๋“œ๋Š” ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

4.2 Mutual Exclusion (์ƒํ˜ธ ๋ฐฐ์ œ)

Critical section์˜ ํ•ต์‹ฌ ์„ฑ์งˆ์ด mutual exclusion(์ƒํ˜ธ ๋ฐฐ์ œ) โ€” ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ทธ ๊ตฌ๊ฐ„์„ ์‹คํ–‰ ์ค‘์ผ ๋•Œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ๋“ค์–ด์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. โ€œMutexโ€๋ผ๋Š” ์ด๋ฆ„๋„ MUTual EXclusion์—์„œ ์™”์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
[์Šค๋ ˆ๋“œ A]              [๋ฝ ์ƒํƒœ]              [์Šค๋ ˆ๋“œ B]
                       UNLOCKED
lock() โ†’ ์„ฑ๊ณต          LOCKED by A
g_count++             LOCKED by A             lock() โ†’ ๋Œ€๊ธฐ (๋ธ”๋ก)
unlock()              UNLOCKED                lock() โ†’ ๊นจ์–ด๋‚จ, ์„ฑ๊ณต
                      LOCKED by B             g_count++
                      UNLOCKED                unlock()

4.3 ์ข‹์€ critical section์˜ ์กฐ๊ฑด (DijkstraยทHoare ๊ณ ์ „ ์ด๋ก )

์กฐ๊ฑด์˜๋ฏธ
Mutual Exclusionํ•œ ์‹œ์ ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ critical section ์•ˆ์— ์žˆ์Œ
Progress (์ง„ํ–‰)critical section์ด ๋น„์–ด ์žˆ์œผ๋ฉด ๋“ค์–ด๊ฐ€๋ ค๋Š” ์Šค๋ ˆ๋“œ ์ค‘ ๋ˆ„๊ตฐ๊ฐ€๋Š” ๋“ค์–ด๊ฐ€์•ผ ํ•จ
Bounded Waiting (์ œํ•œ๋œ ๋Œ€๊ธฐ)๋“ค์–ด๊ฐ€๋ ค๋Š” ์Šค๋ ˆ๋“œ๋Š” ๋ฌดํ•œ ๋Œ€๊ธฐํ•˜์ง€ ์•Š์•„์•ผ ํ•จ
No Starvation (๊ธฐ์•„ ์—†์Œ)๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฒฐ๊ตญ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ

std::mutex๋‚˜ Windows CRITICAL_SECTION์€ ์ด ๋ชจ๋‘๋ฅผ OS๊ฐ€ ์ฑ…์ž„์ง‘๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ชจ๋“  ๋ฝ์ด ๊ทธ๋ ‡์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค โ€” SRWLOCK์˜ writer๋Š” reader๊ฐ€ ๊ณ„์† ๋“ค์–ด์˜ค๋ฉด starvation ๊ฐ€๋Šฅ. ์šฐ์„ ์ˆœ์œ„ ๋ฝ์€ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ starvation ๊ฐ€๋Šฅ.

4.4 Critical Section์˜ ํฌ๊ธฐ โ€” ์งง์„์ˆ˜๋ก ์ข‹๋‹ค

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ๋‚˜์œ ์˜ˆ โ€” critical section์ด ๋„ˆ๋ฌด ๊น€
void ProcessItem(Item* item) {
    mtx.lock();
    auto heavy = ComputeHeavyData(item);    // โ† 100ms ๊ฑธ๋ฆผ. ๊ทธ ๋™์•ˆ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ ๋ชจ๋‘ ๋Œ€๊ธฐ
    sharedList.push_back(heavy);
    mtx.unlock();
}

// ์ข‹์€ ์˜ˆ โ€” ๊ณต์œ  ์ž์› ์ ‘๊ทผ๋งŒ critical section ์•ˆ์—
void ProcessItem(Item* item) {
    auto heavy = ComputeHeavyData(item);    // โ† ๋ฝ ๋ฐ–์—์„œ ๊ณ„์‚ฐ
    mtx.lock();
    sharedList.push_back(heavy);
    mtx.unlock();
}

Critical section์˜ ํฌ๊ธฐ๊ฐ€ ์ค„์–ด๋“ค๋ฉด lock contention(๋ฝ ๊ฒฝํ•ฉ) ์ด ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. ๊ฒฝํ•ฉ์ด ๋งŽ์œผ๋ฉด ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์ด ํญ์ฆํ•ด์„œ single-thread ์ฝ”๋“œ๋ณด๋‹ค ๋” ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค โ€” convoy effect.

4.5 RAII ํŒจํ„ด โ€” std::lock_guard / std::scoped_lock

1
2
3
4
5
6
void worker() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);   // ์ƒ์„ฑ ์‹œ lock, ์†Œ๋ฉธ ์‹œ unlock
        g_count++;
    }
}

lock_guard๋Š” RTTIยทRAII(9)์—์„œ ๋ณธ ํŒจํ„ด ๊ทธ๋Œ€๋กœ. ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ์ž๋™์œผ๋กœ unlock๋˜๋ฏ€๋กœ ์˜ˆ์™ธ๊ฐ€ ๋˜์ ธ์ ธ๋„ unlock ๋ˆ„๋ฝ์ด ์—†์Šต๋‹ˆ๋‹ค. C++ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์ฝ”๋“œ์—์„œ raw lock()/unlock()์„ ์ง์ ‘ ๋ถ€๋ฅด๋Š” ๊ฑด ๊ฑฐ์˜ ํ•ญ์ƒ ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

scoped_lock(C++17)์€ ์—ฌ๋Ÿฌ mutex๋ฅผ deadlock-free๋กœ ๋™์‹œ ์ž ๊ธˆ:

1
2
3
4
5
void Transfer(Account& a, Account& b, int amount) {
    std::scoped_lock lock(a.mtx, b.mtx);   // deadlock-free ๋™์‹œ ๋ฝ
    a.balance -= amount;
    b.balance += amount;
}

std::scoped_lock์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋‘ ๋ฝ์„ try-lock + ๋ฐฑ์˜คํ”„ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์žก์•„์„œ deadlock์„ ํšŒํ”ผํ•ฉ๋‹ˆ๋‹ค (deadlock 4์กฐ๊ฑด์˜ โ€œ์ˆœํ™˜ ๋Œ€๊ธฐโ€๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ์ฐจ๋‹จ).


5. ๋™๊ธฐํ™” ๊ฐ์ฒด ์นดํƒˆ๋กœ๊ทธ โ€” MutexยทSemaphoreยทCritical SectionยทSRWLockยทEventยทCondition Variable

5.1 Mutex (๋ฐฐํƒ€์  ๋ฝ)

๊ฐ€์žฅ ๊ธฐ๋ณธ. ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์žก์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฐํƒ€์  ๋ฝ.

์ข…๋ฅ˜APIํŠน์ง•
std::mutexC++11 ํ‘œ์ค€RAII ๋ž˜ํผ์™€ ํ•จ๊ป˜. ์žฌ์ง„์ž… ๋ถˆ๊ฐ€
std::recursive_mutexC++11๊ฐ™์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์ž ๊ธˆ ๊ฐ€๋Šฅ. ๋ณดํ†ต ์„ค๊ณ„ ๊ฒฐํ•จ ์‹ ํ˜ธ
std::timed_mutexC++11try_lock_for / try_lock_until ์ง€์›
Windows CRITICAL_SECTIONWin32์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ . ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ํ•œ์ •
Windows Mutex (์ปค๋„)CreateMutexํ•ญ์ƒ ์ปค๋„. ์ด๋ฆ„ ๋ถ€์—ฌ ์‹œ ํ”„๋กœ์„ธ์Šค ๊ฐ„
POSIX pthread_mutex_tPOSIX๊ธฐ๋ณธยทrecursiveยทerrorcheckยทrobust ์ข…๋ฅ˜
1
2
3
4
5
std::mutex mtx;
{
    std::lock_guard<std::mutex> lock(mtx);
    // critical section
}  // ์ž๋™ unlock

5.2 Semaphore (์นด์šดํŒ… ๋ฝ)

N๊ฐœ ๋™์‹œ ์ ‘๊ทผ ํ—ˆ์šฉ. ์นด์šดํ„ฐ๊ฐ€ ์Œ์ˆ˜๊ฐ€ ๋˜๋ฉด wait, ์–‘์ˆ˜๋ฉด ์ง„ํ–‰.

1
2
3
4
5
6
7
// C++20๋ถ€ํ„ฐ ํ‘œ์ค€
std::counting_semaphore<10> sem(3);   // ์ตœ๋Œ€ 10, ์ดˆ๊ธฐ 3
// ๋˜๋Š” std::binary_semaphore (0/1๋งŒ ๊ฐ€๋Šฅ, mutex์™€ ๋น„์Šท)

sem.acquire();   // ์นด์šดํ„ฐ -1, 0 ์ดํ•˜๋ฉด ๋Œ€๊ธฐ
// ... critical section (์ตœ๋Œ€ 3๊ฐœ ์Šค๋ ˆ๋“œ ๋™์‹œ ์ง„์ž…)
sem.release();   // ์นด์šดํ„ฐ +1, ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ ๊นจ์›€

์‚ฌ์šฉ ์‚ฌ๋ก€:

  • ์ž์› ํ’€(connection pool): ๋™์‹œ ์ ‘๊ทผ N๊ฐœ๋กœ ์ œํ•œ
  • ์ƒ์‚ฐ์ž-์†Œ๋น„์ž: ๋นˆ ์Šฌ๋กฏ ์นด์šดํ„ฐยท์ฐฌ ์Šฌ๋กฏ ์นด์šดํ„ฐ
  • ์“ฐ๋กœํ‹€๋ง: ์ดˆ๋‹น N๊ฐœ ์ฒ˜๋ฆฌ ์ œํ•œ

5.3 Critical Section (Windows)

Windows์˜ ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„  mutex. ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ์•ˆ ํ•œ์ •์ด์ง€๋งŒ ๋งค์šฐ ๋น ๋ฆ…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
// ๋˜๋Š” InitializeCriticalSectionEx๋กœ spin count ์ง€์ •
InitializeCriticalSectionAndSpinCount(&cs, 4000);

EnterCriticalSection(&cs);
// critical section
LeaveCriticalSection(&cs);

DeleteCriticalSection(&cs);

๋™์ž‘:

  1. ๋ฝ์ด ๋น„์–ด ์žˆ์œผ๋ฉด โ†’ atomic ๋ช…๋ น์œผ๋กœ ์ฆ‰์‹œ ์žก์Œ (์ˆ˜์‹ญ ns)
  2. ๋ฝ์ด ์žกํ˜€ ์žˆ์œผ๋ฉด โ†’ spin count๋งŒํผ busy-wait
  3. spin count ๋๋‚˜๋„ ์•ˆ ํ’€๋ฆฌ๋ฉด โ†’ ์ปค๋„ ์ง„์ž…, ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ

Spin count๋Š” ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ™˜๊ฒฝ์—์„œ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค โ€” ์งง๊ฒŒ ์žกํ˜”๋‹ค ํ’€๋ฆด ๋ฝ์ด๋ฉด ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)๋ณด๋‹ค spin์ด ๋น ๋ฆ…๋‹ˆ๋‹ค.

5.4 SRWLock (Slim Reader/Writer Lock, Windows Vista+)

์ฝ๊ธฐ๋Š” ๋‹ค์ค‘ยท์“ฐ๊ธฐ๋Š” ๋ฐฐํƒ€. std::shared_mutex์˜ Windows ๊ตฌํ˜„ ๊ธฐ๋ฐ˜.

1
2
3
4
5
6
7
8
9
10
11
12
SRWLOCK srw;
InitializeSRWLock(&srw);

// ์“ฐ๊ธฐ ๋ฝ
AcquireSRWLockExclusive(&srw);
// ... ์“ฐ๊ธฐ critical section (ํ•œ ์Šค๋ ˆ๋“œ๋งŒ)
ReleaseSRWLockExclusive(&srw);

// ์ฝ๊ธฐ ๋ฝ
AcquireSRWLockShared(&srw);
// ... ์ฝ๊ธฐ critical section (์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ ๋™์‹œ)
ReleaseSRWLockShared(&srw);

C++ ํ‘œ์ค€ ๋“ฑ๊ฐ€๋ฌผ:

1
2
3
4
5
6
7
8
9
10
11
12
13
std::shared_mutex smtx;

// ์“ฐ๊ธฐ
{
    std::unique_lock<std::shared_mutex> lock(smtx);
    // ... ์“ฐ๊ธฐ
}

// ์ฝ๊ธฐ
{
    std::shared_lock<std::shared_mutex> lock(smtx);
    // ... ์ฝ๊ธฐ
}

reader-heavy ์›Œํฌ๋กœ๋“œ(์ฝ๊ธฐ ๋งŽ๊ณ  ์“ฐ๊ธฐ ์ ์Œ)์— ์ ํ•ฉ โ€” ๋ชจ๋“  reader๊ฐ€ ๋™์‹œ ์ง„ํ–‰. writer starvation ์œ„ํ—˜ ์žˆ์Œ(reader๊ฐ€ ๊ณ„์† ๋“ค์–ด์˜ค๋ฉด writer๊ฐ€ ์˜์›ํžˆ ๋ชป ์žก์Œ). ์ •์ฑ…์€ OS ๊ตฌํ˜„์— ๋”ฐ๋ผ ๋‹ค๋ฆ„.

5.5 Event (Windows) / Condition Variable

์ƒํƒœ ์•Œ๋ฆผ ์šฉ. critical section๊ณผ ๋‹ฌ๋ฆฌ ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ๊ฐ€ ์•„๋‹ˆ๋ผ โ€œ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚ฌ๋‹คโ€๋ฅผ ์•Œ๋ฆผ.

1
2
3
4
5
6
7
8
9
10
// Windows Event
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// ๋‘ ๋ฒˆ์งธ ์ธ์ž FALSE: auto-reset (ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ๊นจ์›€)
// ์„ธ ๋ฒˆ์งธ ์ธ์ž FALSE: ์ดˆ๊ธฐ ์ƒํƒœ non-signaled

// ํ•œ ์Šค๋ ˆ๋“œ: ๋Œ€๊ธฐ
WaitForSingleObject(hEvent, INFINITE);

// ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ: ์‹ ํ˜ธ
SetEvent(hEvent);

C++ ํ‘œ์ค€์˜ condition variable์€ mutex์™€ ์ง์ง€์–ด ์”๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

// ๋Œ€๊ธฐ ์ธก
void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });   // ready==true ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (atomicํ•˜๊ฒŒ unlock+sleep)
    // ... ready ๋˜๋ฉด ๊นจ์–ด๋‚จ, ๋‹ค์‹œ lock ์žก์Œ
}

// ์‹ ํ˜ธ ์ธก
void producer() {
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();   // ๋˜๋Š” notify_all()
}

cv.wait์˜ ์ˆ ์–ด(predicate) ์ธ์ž๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค โ€” spurious wakeup(๊ฐ€์งœ ๊นจ์–ด๋‚จ) ํšŒํ”ผ์šฉ. CV๋Š” OS์˜ ๋ณด์žฅ ๋ถ€์กฑ์œผ๋กœ ๊นจ์–ด๋‚ฌ์„ ๋•Œ ์ˆ ์–ด๋ฅผ ์žฌํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5.6 ๋™๊ธฐํ™” ๊ฐ์ฒด ๋น„๊ต ํ‘œ

๊ฐ์ฒด๋™์‹œ ์ ‘๊ทผ๋น„์šฉ (๊ฒฝํ•ฉ ์—†์Œ)ํ”„๋กœ์„ธ์Šค ๊ฐ„์‚ฌ์šฉ์ฒ˜
std::mutex / Critical Section1 (๋ฐฐํƒ€)์ˆ˜์‹ญ nsX (Critical Section)์ผ๋ฐ˜ ๋ฝ
std::recursive_mutex1 (๊ฐ™์€ ์Šค๋ ˆ๋“œ ์žฌ์ง„์ž…)์ˆ˜์‹ญ nsX์žฌ๊ท€ ํ˜ธ์ถœ
std::shared_mutex / SRWLockN (read) / 1 (write)์ˆ˜์‹ญ nsXreader-heavy
std::counting_semaphoreN (์ง€์ •)์ˆ˜์‹ญ ns(๊ตฌํ˜„์— ๋”ฐ๋ผ)์ž์› ํ’€
Mutex ์ปค๋„ ๊ฐ์ฒด (CreateMutex)11~3 ฮผsO (์ด๋ฆ„ ๋ถ€์—ฌ)IPC(22) ํšŒ๊ท€
Semaphore ์ปค๋„ ๊ฐ์ฒดN1~3 ฮผsOIPC(22)
Event- (์•Œ๋ฆผ)1~3 ฮผsO (์ด๋ฆ„)์•Œ๋ฆผยท์ผํšŒ์„ฑ ๋™๊ธฐํ™”
Condition Variable- (๋Œ€๊ธฐ)1~3 ฮผsX (๋ณดํ†ต)์กฐ๊ฑด ๋งŒ์กฑ ๋Œ€๊ธฐ

6. Lock-freeยทatomicยทCAS โ€” ๋ฝ ์—†๋Š” ๋™๊ธฐํ™”

6.1 ์™œ lock-free์ธ๊ฐ€

๋ฝ์€ ๋น„์šฉ์ด ํฝ๋‹ˆ๋‹ค โ€” ๊ฒฝํ•ฉ ์‹œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21) ๋ฐœ์ƒ, ๋ฐ๋“œ๋ฝ ์œ„ํ—˜, priority inversion. lock-free๋Š” ๋ฝ ์—†์ด ๋™๊ธฐํ™”ํ•˜๋Š” ๊ธฐ๋ฒ•์œผ๋กœ, CPU์˜ atomic instruction์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.

lock-free์˜ ์ •ํ™•ํ•œ ์ •์˜: ์–ด๋–ค ์‹œ์ ์—์„œ๋„ ์ ์–ด๋„ ํ•œ ์Šค๋ ˆ๋“œ๋Š” finite step ์•ˆ์— ์ง„ํ–‰๋œ๋‹ค (์‹œ์Šคํ…œ ์ „์ฒด๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š์Œ). wait-free๋Š” ๋” ๊ฐ•ํ•จ: ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ finite step ์•ˆ์— ์ง„ํ–‰๋œ๋‹ค.

๋‹จ๊ณ„๋ณด์žฅ
Blocking (๋ฝ ๊ธฐ๋ฐ˜)ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฉˆ์ถ”๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋„ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์Œ (deadlock ๊ฐ€๋Šฅ)
Obstruction-free๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ๊ฐ„์„ญ์ด ์—†์œผ๋ฉด finite step์— ์ง„ํ–‰
Lock-free์‹œ์Šคํ…œ ์ „์ฒด๋กœ ๋ณด๋ฉด ์ ์–ด๋„ ํ•˜๋‚˜๋Š” ํ•ญ์ƒ ์ง„ํ–‰
Wait-free๋ชจ๋“  ๊ฐœ๋ณ„ ์Šค๋ ˆ๋“œ๊ฐ€ finite step์— ์ง„ํ–‰ (๊ฐ€์žฅ ๊ฐ•ํ•จ)

๋Œ€๋ถ€๋ถ„์˜ lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋Š” wait-free๊นŒ์ง€๋Š” ๋ชป ๊ฐ€๊ณ  lock-free์— ๋จธ๋ญ…๋‹ˆ๋‹ค โ€” wait-free๊ฐ€ ํ›จ์”ฌ ์–ด๋ ค์›€.

6.2 atomic โ€” ๋‹จ์ผ ๋ช…๋ น ๋™๊ธฐํ™”

1
2
3
4
5
6
std::atomic<int> count{0};

// ์•ˆ์ „ โ€” atomic instruction
count.fetch_add(1);                  // ๋˜๋Š” count++
int v = count.load();                // ๋˜๋Š” (int)count
count.store(42);                     // ๋˜๋Š” count = 42

std::atomic<T>์˜ ์—ฐ์‚ฐ์€ CPU์˜ ๋‹จ์ผ ๋ช…๋ น์œผ๋กœ ๋๋‚˜๊ฑฐ๋‚˜, LOCK prefix๊ฐ€ ๋ถ™์€ RMW(read-modify-write) ๋ช…๋ น์œผ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
count.fetch_add(1)๋ฅผ x86 ์–ด์…ˆ๋ธ”๋ฆฌ๋กœ:
   lock add [count], 1        ; LOCK prefix โ†’ ๋‹ค๋ฅธ CPU ์ฝ”์–ด๊ฐ€ ๋ผ์–ด๋“ค์ง€ ๋ชปํ•จ

count++ (๋น„ atomic)์„ x86 ์–ด์…ˆ๋ธ”๋ฆฌ๋กœ:
   mov eax, [count]            ; โ† ์—ฌ๊ธฐ์„œ ๋ผ์–ด๋“ค ์ˆ˜ ์žˆ์Œ
   inc eax
   mov [count], eax

LOCK prefix๋Š” ๋ฒ„์Šค ๋ฝ(bus lock) ๋˜๋Š” ์บ์‹œ ๋ผ์ธ ๋ฝ(cache line lock) ์„ ๊ฑธ์–ด ๋‹ค๋ฅธ ์ฝ”์–ด๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๋น„์šฉ์€ ์ˆ˜~์ˆ˜์‹ญ ns๋กœ ๋ฝ์— ๋น„ํ•ด ๋งค์šฐ ์ €๋ ดํ•ฉ๋‹ˆ๋‹ค.

6.3 CAS โ€” Compare-And-Swap

lock-free์˜ ํ•ต์‹ฌ ๋ช…๋ น. โ€œ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’์ด expected๋ฉด desired๋กœ ๋ฐ”๊พธ๊ณ  true ๋ฐ˜ํ™˜, ์•„๋‹ˆ๋ฉด ๊ทธ ๋ฉ”๋ชจ๋ฆฌ์˜ ํ˜„์žฌ ๊ฐ’์„ expected์— ์“ฐ๊ณ  false ๋ฐ˜ํ™˜โ€.

1
2
3
4
5
6
7
std::atomic<int> value{100};

int expected = 100;
int desired = 200;
bool success = value.compare_exchange_strong(expected, desired);
// value == 100์ด๋ฉด โ†’ 200์œผ๋กœ ๋ณ€๊ฒฝ, success = true
// value != 100์ด๋ฉด โ†’ expected์— ํ˜„์žฌ value ์ €์žฅ, success = false

x86 ์–ด์…ˆ๋ธ”๋ฆฌ๋Š” LOCK CMPXCHG. ARM์€ LL/SC (Load-Linked/Store-Conditional).

CAS๋กœ ๋งŒ๋“  lock-free ์นด์šดํ„ฐ:

1
2
3
4
5
6
7
8
std::atomic<int> count{0};

void Increment() {
    int old_val = count.load();
    while (!count.compare_exchange_weak(old_val, old_val + 1)) {
        // ์‹คํŒจ ์‹œ old_val์— ํ˜„์žฌ ๊ฐ’์ด ๋“ค์–ด ์žˆ์Œ, ๋‹ค์‹œ ์‹œ๋„
    }
}

์ด๊ฒŒ ์‚ฌ์‹ค์€ count.fetch_add(1)๊ณผ ๊ฐ™์€ ์ผ์„ ํ•˜์ง€๋งŒ, ์ž„์˜์˜ ์—ฐ์‚ฐ์œผ๋กœ ์ผ๋ฐ˜ํ™” ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
// "๊ฐ’์ด ์Œ์ˆ˜๋ฉด 0์œผ๋กœ ๋งŒ๋“ค๊ธฐ" โ€” fetch_add๋กœ๋Š” ๋ชป ํ•จ, CAS๋กœ ๊ฐ€๋Šฅ
void ClampNonNegative() {
    int old_val = value.load();
    while (old_val < 0) {
        if (value.compare_exchange_weak(old_val, 0)) break;
    }
}

6.4 weak vs strong CAS

๋ณ€ํ˜•๋™์ž‘์‚ฌ์šฉ์ฒ˜
compare_exchange_weakspurious failure ํ—ˆ์šฉ (์‹คํŒจํ•ด๋„ ์ •๋ง ๊ฐ™์€์ง€ ๋ชจ๋ฆ„)๋ฃจํ”„์—์„œ ์‚ฌ์šฉ โ€” ์–ด์ฐจํ”ผ ์žฌ์‹œ๋„
compare_exchange_strong์ง„์งœ ๋น„๊ต ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜๋‹จ์ผ ์‹œ๋„

weak๋Š” ARM ๊ฐ™์€ LL/SC ๋จธ์‹ ์—์„œ ์ธํ„ฐ๋ŸฝํŠธ ๋“ฑ์œผ๋กœ spurious failure๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ๋ช…๋ น์— ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค. ๋ฃจํ”„์—์„œ๋Š” weak๊ฐ€ ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

6.5 Windows Interlocked* ํ•จ์ˆ˜๊ตฐ

C++ std::atomic์ด ๋„์ž…๋˜๊ธฐ ์ „ Windows์˜ atomic API.

WindowsC++ ๋“ฑ๊ฐ€์˜๋ฏธ
InterlockedIncrementfetch_add(1)+1
InterlockedDecrementfetch_sub(1)-1
InterlockedExchangeexchange๊ตํ™˜
InterlockedCompareExchangecompare_exchange_strongCAS
InterlockedAddfetch_add+N
InterlockedAnd / Or / Xorfetch_and/or/xor๋น„ํŠธ ์—ฐ์‚ฐ

std::atomic์˜ Windows ๊ตฌํ˜„์€ ๋ณดํ†ต ์ด ํ•จ์ˆ˜๋“ค ๋˜๋Š” ์ง์ ‘ LOCK prefix ๋ช…๋ น์œผ๋กœ ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค.

6.6 Lock-free ์ž๋ฃŒ๊ตฌ์กฐ์˜ ์˜ˆ โ€” ์Šคํƒ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template<typename T>
class LockFreeStack {
    struct Node { T value; Node* next; };
    std::atomic<Node*> head{nullptr};
public:
    void push(T v) {
        Node* newNode = new Node{v, nullptr};
        Node* oldHead = head.load();
        do {
            newNode->next = oldHead;
        } while (!head.compare_exchange_weak(oldHead, newNode));
        // CAS ์‹คํŒจ ์‹œ oldHead์— ํ˜„์žฌ head๊ฐ€ ๋“ค์–ด์˜ด โ†’ ๋‹ค์‹œ ์‹œ๋„
    }

    bool pop(T& out) {
        Node* oldHead = head.load();
        while (oldHead && !head.compare_exchange_weak(oldHead, oldHead->next))
            ; // CAS ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„
        if (!oldHead) return false;
        out = oldHead->value;
        delete oldHead;   // โš  ABA ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ โ€” 8์ ˆ ์ฐธ์กฐ
        return true;
    }
};

์ด ๋‹จ์ˆœ lock-free ์Šคํƒ์€ ABA ๋ฌธ์ œ ๋•Œ๋ฌธ์— ์‹ค์ „์—์„œ๋Š” ๊ทธ๋Œ€๋กœ ๋ชป ์”๋‹ˆ๋‹ค. tagged pointerยทhazard pointerยทepoch-based reclamation ๊ฐ™์€ ์ถ”๊ฐ€ ๊ธฐ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

6.7 Lock-free๊ฐ€ ์ •๋‹ต์ด ์•„๋‹Œ ์ด์œ 

๋‹จ์ ์„ค๋ช…
์„ค๊ณ„ ๋งค์šฐ ์–ด๋ ค์›€๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์ธํ„ฐ๋ฆฌ๋น™์„ ๊ณ ๋ คํ•ด์•ผ ํ•จ. ๊ฒ€์ฆ ๋„๊ตฌ ํ•„์š”(TLA+ยทSpin)
debug ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅ์žฌํ˜„ ์•ˆ ๋˜๋Š” ๋ฒ„๊ทธ. memory model ์œ„๋ฐ˜ ์‹œ ๋””๋ฒ„๊ฑฐ๊ฐ€ ๋„์™€์ฃผ์ง€ ๋ชปํ•จ
๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜ ๋ฌธ์ œ์œ„ delete oldHead์ฒ˜๋Ÿผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์Œ
memory ordering ์ง์ ‘ ์ง€์ •acquire/release๋ฅผ ์ž˜๋ชป ์“ฐ๋ฉด ์ผ๊ด€์„ฑ ๊นจ์ง
CPU์— ๋”ฐ๋ฅธ ๋น„์šฉLOCK prefix๋Š” cache coherence traffic์„ ๋งŒ๋“ค์–ด ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ™•์žฅ์„ฑ ์ €ํ•ด

๊ทธ๋ž˜์„œ ์‹ค๋ฌด์—์„  ๊ณ ๋„๋กœ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Boost.Lockfree, folly, concurrentqueue)๋ฅผ ์“ฐ์ง€, ์ง์ ‘ lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๋งŒ๋“œ๋Š” lock-free๋Š” ๊ฑฐ์˜ ํ•ญ์ƒ ๋ฝ ๊ธฐ๋ฐ˜๋ณด๋‹ค ๋А๋ฆฝ๋‹ˆ๋‹ค.


7. Memory OrderingยทMemory Barrier โ€” acquire/release ํŽ˜์–ด

7.1 ์™œ memory ordering์ด ๋ฌธ์ œ์ธ๊ฐ€

ํ˜„๋Œ€ CPU๋Š” ์„ฑ๋Šฅ์„ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค:

  • ์ปดํŒŒ์ผ๋Ÿฌ ์žฌ๋ฐฐ์น˜ โ€” ์ตœ์ ํ™” ์‹œ ๋ช…๋ น ์ˆœ์„œ ๋ณ€๊ฒฝ
  • out-of-order execution โ€” CPU๊ฐ€ dependency ์—†๋Š” ๋ช…๋ น์„ ๋ณ‘๋ ฌ ์‹คํ–‰
  • store buffer โ€” ์“ฐ๊ธฐ๊ฐ€ ์ฆ‰์‹œ ์บ์‹œ์— ์•ˆ ๊ฐ€๊ณ  store buffer์— ๋จธ๋ฌพ
  • cache coherence delay โ€” ํ•œ ์ฝ”์–ด์˜ ์“ฐ๊ธฐ๊ฐ€ ๋‹ค๋ฅธ ์ฝ”์–ด์— ๋ณด์ด๊ธฐ๊นŒ์ง€ ์‹œ๊ฐ„
1
2
3
4
5
6
7
8
// ์Šค๋ ˆ๋“œ A
data = 42;          // โ‘  store
ready = true;       // โ‘ก store

// ์Šค๋ ˆ๋“œ B
if (ready) {        // โ‘ข load
    use(data);      // โ‘ฃ load โ€” data ๊ฐ€ 42 ์ผ๊นŒ?
}

์ง๊ด€์ ์œผ๋ก  โ€œready๊ฐ€ true๋ฉด data๋Š” 42โ€์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ:

  • ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ โ‘ โ‘ก๋ฅผ โ‘ก โ†’ โ‘  ์ˆœ์„œ๋กœ ์žฌ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Œ
  • CPU๊ฐ€ ๊ฐ™์€ ์ผ์„ ํ•  ์ˆ˜ ์žˆ์Œ
  • ์บ์‹œ ์ผ๊ด€์„ฑ ์ง€์—ฐ์œผ๋กœ B๊ฐ€ ready=true๋Š” ๋ดค์ง€๋งŒ data=42๋Š” ์•„์ง ๋ชป ๋ด„

โ†’ B์—์„œ use(data)๊ฐ€ 42๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฐ’(์˜ˆ: 0)์„ ๋ณผ ์ˆ˜ ์žˆ์Œ.

7.2 C++ memory_order 6๋‹จ๊ณ„

1
2
3
4
5
6
7
8
enum memory_order {
    memory_order_relaxed,   // ์ˆœ์„œ ๋ณด์žฅ ์—†์Œ
    memory_order_consume,   // (deprecated ๊ถŒ์žฅ โ€” relaxed๋กœ fallback)
    memory_order_acquire,   // load์— ๋ถ™์Œ โ€” ์ดํ›„ ์ ‘๊ทผ์ด ์•ž์œผ๋กœ ์•ˆ ์˜ด
    memory_order_release,   // store์— ๋ถ™์Œ โ€” ์ด์ „ ์ ‘๊ทผ์ด ๋’ค๋กœ ์•ˆ ๊ฐ
    memory_order_acq_rel,   // RMW์— ๋ถ™์Œ โ€” acquire+release
    memory_order_seq_cst    // ๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ชจ๋ธ (๊ธฐ๋ณธ๊ฐ’)
};

7.3 ๊ฐ€์žฅ ํ”ํ•œ ํŒจํ„ด โ€” acquire/release pair

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::atomic<bool> ready{false};
int data = 0;

// Producer ์Šค๋ ˆ๋“œ
data = 42;
ready.store(true, std::memory_order_release);
//                         ^^^^^^^^^^
//   "์ด์ „์˜ ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ์“ฐ๊ธฐ(data=42)๊ฐ€ ์ด store ์ด์ „์— ์™„๋ฃŒ"

// Consumer ์Šค๋ ˆ๋“œ
while (!ready.load(std::memory_order_acquire))
    ;
//                         ^^^^^^^^^^
//   "์ด load ์ดํ›„์˜ ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ์ฝ๊ธฐ๊ฐ€ ์ด load ์ดํ›„์— ์‹œ์ž‘"
use(data);   // โ† ์•ˆ์ „. 42 ๋ณด์žฅ

release store โ†’ acquire load๊ฐ€ ํ•œ ์ง์ผ ๋•Œ happens-before ๊ด€๊ณ„๊ฐ€ ์„ฑ๋ฆฝํ•ฉ๋‹ˆ๋‹ค โ€” release ์ด์ „์˜ ๋ชจ๋“  ์“ฐ๊ธฐ๊ฐ€ acquire ์ดํ›„์˜ ๋ชจ๋“  ์ฝ๊ธฐ์—์„œ ๋ณด์ž…๋‹ˆ๋‹ค.

7.4 memory_order_seq_cst (sequential consistency)

๊ธฐ๋ณธ๊ฐ’. ๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ชจ๋ธ โ€” ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๋ชจ๋“  SC atomic ์—ฐ์‚ฐ์„ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ด…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
std::atomic<int> x{0}, y{0};

// ์Šค๋ ˆ๋“œ A
x.store(1);     // SC
int r1 = y.load();

// ์Šค๋ ˆ๋“œ B
y.store(1);     // SC
int r2 = x.load();

// SC ํ•˜์—์„œ r1=0 && r2=0 ๋ถˆ๊ฐ€๋Šฅ
//   (๋‘˜ ๋‹ค 0์ด๋ฉด ๋‘ store๊ฐ€ ๋‘˜ ๋‹ค ์ž๊ธฐ load๋ณด๋‹ค ๋Šฆ์€ ์…ˆ, ์ผ๊ด€๋œ ์ˆœ์„œ ์—†์Œ)
// relaxed ํ•˜์—์„œ๋Š” r1=0 && r2=0 ๊ฐ€๋Šฅ

SC๋Š” ๊ฐ€์žฅ ์•ˆ์ „ํ•˜์ง€๋งŒ ๊ฐ€์žฅ ๋น„์Œ‰๋‹ˆ๋‹ค โ€” ๋ชจ๋“  ์ฝ”์–ด์— memory fence๋ฅผ brodcastํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ARM ๊ฐ™์€ weak memory model์—์„  SC๊ฐ€ ๋งค์šฐ ๋น„์‹ธ๊ณ , x86์€ TSO๋ผ ์ƒ๋Œ€์ ์œผ๋กœ ์‹ธ์ง€๋งŒ ๊ทธ๋ž˜๋„ ๋น„์Œ‰๋‹ˆ๋‹ค.

7.5 memory_order_relaxed โ€” ์ˆœ์„œ ๋ณด์žฅ ์—†์Œ

์ˆœ์„œ๋Š” ๋ณด์žฅ ์•ˆ ํ•˜๊ณ  ์›์ž์„ฑ๋งŒ ๋ณด์žฅ.

1
2
3
4
std::atomic<int> counter{0};

// ๋‹จ์ˆœ ์นด์šดํ„ฐ โ€” ์ˆœ์„œ ๋ฌด๊ด€
counter.fetch_add(1, std::memory_order_relaxed);

๋‹ค๋ฅธ ๋ณ€์ˆ˜์™€์˜ ์ˆœ์„œ ๊ด€๊ณ„๊ฐ€ ํ•„์š” ์—†์„ ๋•Œ๋งŒ. ํ†ต๊ณ„ ์นด์šดํ„ฐยท์ฐธ์กฐ ์นด์šดํ„ฐ(shared_ptr์˜ ์ฆ๊ฐ€ ๋ถ€๋ถ„) ๋“ฑ์— ์ ํ•ฉ. ์ฐธ์กฐ ์นด์šดํ„ฐ์˜ ๊ฐ์†Œ๋Š” release-acquire์—ฌ์•ผ ํ•จ (์†Œ๋ฉธ ์ง์ „ ๋งˆ์ง€๋ง‰ ์“ฐ๊ธฐ ๊ฐ€์‹œ์„ฑ ํ•„์š”).

7.6 ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ ์ฐจ์ด

์•„ํ‚คํ…์ฒ˜๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ์žฌ๋ฐฐ์น˜ ํ—ˆ์šฉ
x86 / x64TSO (Total Store Order)storeโ†’load๋งŒ ์žฌ๋ฐฐ์น˜. ๋น„๊ต์  ๊ฐ•ํ•จ
ARM / ARM64Weak๊ฑฐ์˜ ๋ชจ๋“  ์žฌ๋ฐฐ์น˜ ํ—ˆ์šฉ
RISC-VWeak (RVWMO)ARM๊ณผ ๋น„์Šท
POWERWeak๋งค์šฐ ์•ฝํ•จ

x86์—์„œ ์ž˜ ๋Œ๋˜ ์ฝ”๋“œ๊ฐ€ ARM์—์„œ race๋กœ ๊นจ์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ”ํ•ฉ๋‹ˆ๋‹ค โ€” x86์ด ๋ฌด์˜์‹์ ์œผ๋กœ acquire/release๋ฅผ ์ผ๋ถ€ ๋ณด์žฅํ•ด์คฌ๊ธฐ ๋•Œ๋ฌธ. ๊ทธ๋ž˜์„œ cross-platform ์ฝ”๋“œ๋Š” ๋ช…์‹œ์  memory_order๊ฐ€ ํ•„์ˆ˜.

7.7 Memory Barrier ์ง์ ‘ ํ˜ธ์ถœ

1
2
3
4
5
6
7
8
9
10
11
12
// Windows
MemoryBarrier();           // full barrier

// C++11
std::atomic_thread_fence(std::memory_order_seq_cst);   // full
std::atomic_thread_fence(std::memory_order_acquire);
std::atomic_thread_fence(std::memory_order_release);

// x86 ์ธ๋ผ์ธ
_mm_mfence();   // full memory fence
_mm_sfence();   // store fence
_mm_lfence();   // load fence

์ง์ ‘ ํ˜ธ์ถœ์€ ๊ฑฐ์˜ ์•ˆ ์”๋‹ˆ๋‹ค โ€” atomic ์—ฐ์‚ฐ์— memory_order๋ฅผ ๋ถ™์ด๋Š” ๊ฒŒ ํ‘œ์ค€ ๋ฐฉ์‹.


8. ABA ๋ฌธ์ œ โ€” Lock-free์˜ ํ•จ์ •

8.1 ์‹œ๋‚˜๋ฆฌ์˜ค

1
2
3
4
5
6
7
8
9
10
// LockFreeStack์˜ pop
bool pop(T& out) {
    Node* oldHead = head.load();
    while (oldHead && !head.compare_exchange_weak(oldHead, oldHead->next))
        ;
    if (!oldHead) return false;
    out = oldHead->value;
    delete oldHead;
    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
์ดˆ๊ธฐ: head โ†’ A โ†’ B โ†’ C โ†’ null

์Šค๋ ˆ๋“œ 1: oldHead = A๋ฅผ load
       (CAS ์ง์ „์— ์Šค์œ„์น˜)

์Šค๋ ˆ๋“œ 2: pop() โ†’ A ์ œ๊ฑฐ, head โ†’ B
์Šค๋ ˆ๋“œ 2: pop() โ†’ B ์ œ๊ฑฐ, head โ†’ C
์Šค๋ ˆ๋“œ 2: push(D)
์Šค๋ ˆ๋“œ 2: free(A), ๊ทธ ๋ฉ”๋ชจ๋ฆฌ์— new๋กœ ๋‹ค์‹œ ์žกํ˜”๋Š”๋ฐ ์šฐ์—ฐํžˆ ๊ฐ™์€ ์ฃผ์†Œ โ†’ push(A')
          โ€ป A' ๋ผ๊ณ  ํ‘œ๊ธฐํ–ˆ์ง€๋งŒ ์ฃผ์†Œ๋Š” ๊ฐ™์Œ
          head โ†’ A' โ†’ C โ†’ null    (A'->next = C)

์Šค๋ ˆ๋“œ 1 ๊นจ์–ด๋‚จ: head.compare_exchange_weak(oldHead=A, oldHead->next=B)
              CAS๋Š” head==A๋กœ ๋ณธ๋‹ค (์ฃผ์†Œ๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ) โ†’ ์„ฑ๊ณต
              head๋ฅผ B๋กœ ๋ฐ”๊ฟˆ
              ํ•˜์ง€๋งŒ B๋Š” ์ด๋ฏธ free๋œ ๋ฉ”๋ชจ๋ฆฌ! โ† ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ(10) ํšŒ๊ท€

CAS๋Š” ๊ฐ’(์ฃผ์†Œ)๋งŒ ๋น„๊ตํ•˜์ง€ โ€œ๊ทธ ์‚ฌ์ด์— ๋‹ค๋ฅธ ์ผ์ด ์ผ์–ด๋‚ฌ๋Š”์ง€โ€๋Š” ๋ชจ๋ฆ…๋‹ˆ๋‹ค. Aโ†’Bโ†’A๋กœ ๋Œ์•„์˜จ ๊ฑธ ์•Œ์•„์ฑ„์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค โ€” ์ด๊ฒŒ ABA ๋ฌธ์ œ.

8.2 ํšŒํ”ผ 1 โ€” Tagged Pointer (๋ฒ„์ „ ์นด์šดํ„ฐ)

1
2
3
4
5
6
7
8
9
10
struct TaggedPtr {
    Node* ptr;
    uintptr_t tag;   // ๋งค ์ˆ˜์ •๋งˆ๋‹ค ์ฆ๊ฐ€
};
std::atomic<TaggedPtr> head;

// pop
TaggedPtr old = head.load();
TaggedPtr next_tagged{old.ptr->next, old.tag + 1};
head.compare_exchange_weak(old, next_tagged);

๋งค ์ˆ˜์ •๋งˆ๋‹ค tag๊ฐ€ 1 ์ฆ๊ฐ€ํ•˜๋ฏ€๋กœ, A โ†’ B โ†’ A๋กœ ๋Œ์•„์™€๋„ tag๊ฐ€ ๋‹ค๋ฆ„. CAS๊ฐ€ ๋‹ค๋ฅด๋‹ค๊ณ  ์ธ์‹.

64๋น„ํŠธ ๋จธ์‹ ์—์„œ๋Š” ํฌ์ธํ„ฐ + 16~32๋น„ํŠธ tag๋กœ double-word CAS(x86์˜ LOCK CMPXCHG16B)๋ฅผ ์”๋‹ˆ๋‹ค.

8.3 ํšŒํ”ผ 2 โ€” Hazard Pointer

๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ โ€œ์ง€๊ธˆ ์ฝ๊ณ  ์žˆ๋Š” ํฌ์ธํ„ฐโ€๋ฅผ hazard pointer ๋ฐฐ์—ด์— ๋“ฑ๋ก. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšŒ์ˆ˜ํ•˜๋ ค๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ hazard pointer๋ฅผ ๋ชจ๋‘ ๊ฒ€์‚ฌํ•ด์„œ ์•„๋ฌด๋„ ์•ˆ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ.

1
2
3
4
5
์Šค๋ ˆ๋“œ 1: hazard[1] = A   (A๋ฅผ ์ฝ์„ ๊ฑฐ์ž„)
์Šค๋ ˆ๋“œ 2: A๋ฅผ list์—์„œ ์ œ๊ฑฐ
์Šค๋ ˆ๋“œ 2: free(A)? โ†’ hazard[1] = A ์ด๋ฏ€๋กœ ๋ชป ํ•จ, retire list์— ๋„ฃ์Œ
์Šค๋ ˆ๋“œ 1: ๋๋‚˜๋ฉด hazard[1] = nullptr
์Šค๋ ˆ๋“œ 2: ๋‹ค์Œ ํšŒ์ˆ˜ ์‹œ retire list ์žฌ๊ฒ€์‚ฌ

์˜ค๋ฒ„ํ—ค๋“œ๋Š” ์žˆ์ง€๋งŒ ABA๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ๋ง‰์Œ.

8.4 ํšŒํ”ผ 3 โ€” Epoch-based Reclamation

์ „์—ญ epoch ์นด์šดํ„ฐ๋ฅผ ๋‘๊ณ , ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์ž๊ธฐ epoch์„ ๊ธฐ๋ก. ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ epoch N ์ดํ›„๋กœ ์ง„์ž…ํ–ˆ์œผ๋ฉด epoch N ์ด์ „์— retire๋œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์•ˆ์ „ํ•˜๊ฒŒ ํšŒ์ˆ˜.

RCU(Read-Copy-Update, Linux ์ปค๋„)๊ฐ€ ์ด ์›๋ฆฌ๋ฅผ ์‚ฌ์šฉ.

8.5 ํšŒํ”ผ 4 โ€” Garbage Collection

GC๊ฐ€ ์žˆ๋Š” ์–ธ์–ด(JavaยทC#)๋Š” ABA๊ฐ€ ์•ˆ ์ผ์–ด๋‚จ โ€” GC๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšŒ์ˆ˜ํ•  ๋•Œ ๋ˆ„๊ฐ€ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ์•ˆ๋‹ค. ๊ทธ๋ž˜์„œ Java์˜ AtomicReference๋Š” ABA ๊ฑฑ์ • ์—†์ด lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ.

C++์€ GC๊ฐ€ ์—†์œผ๋‹ˆ ์œ„ ์„ธ ๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ โ€” lock-free๊ฐ€ ์–ด๋ ค์šด ์ง„์งœ ์ด์œ .

8.6 ๋ฉด์ ‘์—์„œ ๊ฐ•์กฐ ํฌ์ธํŠธ

  • โ€œCAS๊ฐ€ ๊ฐ’๋งŒ ๋น„๊ตํ•˜์ง€ ๋ณ€ํ™” ์ด๋ ฅ์€ ๋ชจ๋ฆ…๋‹ˆ๋‹คโ€
  • โ€œlock-free ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ABA๋งŒ ์•„๋‹ˆ๋ผ memory reclamation ๋ฌธ์ œ๊นŒ์ง€ ๊ฐ™์ด ํ’€์–ด์•ผ ํ•ฉ๋‹ˆ๋‹คโ€
  • โ€œ๊ทธ๋ž˜์„œ ์‹ค๋ฌด์—์„  Boost.Lockfree๋‚˜ folly ๊ฐ™์€ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์”๋‹ˆ๋‹คโ€

9. Deadlock โ€” ๋ฐœ์ƒ 4์กฐ๊ฑด๊ณผ ํšŒํ”ผ ์ „๋žต

9.1 Coffman ์กฐ๊ฑด (Deadlock 4์กฐ๊ฑด)

์กฐ๊ฑด์˜๋ฏธ
โ‘  Mutual Exclusion์ž์›์„ ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‚ฌ์šฉ
โ‘ก Hold and Wait์ž์›์„ ์žก์€ ์ฑ„๋กœ ๋‹ค๋ฅธ ์ž์› ๋Œ€๊ธฐ
โ‘ข No Preemption์ž์›์„ ๊ฐ•์ œ๋กœ ๋นผ์•—์„ ์ˆ˜ ์—†์Œ
โ‘ฃ Circular Wait๋Œ€๊ธฐ ๊ทธ๋ž˜ํ”„์— ์ˆœํ™˜ ๋ฐœ์ƒ

๋„ค ์กฐ๊ฑด์ด ๋ชจ๋‘ ๋งŒ์กฑ๋˜์–ด์•ผ deadlock. ํ•˜๋‚˜๋งŒ ๊นจ๋ฉด deadlock ๋ถˆ๊ฐ€.

9.2 ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ˆ์ œ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
std::mutex mtx_a, mtx_b;

void thread1() {
    mtx_a.lock();
    // โ† ์—ฌ๊ธฐ์„œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜
    mtx_b.lock();
    // ...
    mtx_b.unlock();
    mtx_a.unlock();
}

void thread2() {
    mtx_b.lock();    // โ† ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜ ํ›„ ์—ฌ๊ธฐ ์‹คํ–‰
    // โ† ์—ฌ๊ธฐ์„œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜
    mtx_a.lock();    // โ† thread1์ด mtx_a ์žก๊ณ  ์žˆ์Œ, ๋Œ€๊ธฐ
    // ...
}

// thread1์€ mtx_b๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ , thread2๋Š” mtx_a๋ฅผ ๊ธฐ๋‹ค๋ฆผ
// โ†’ ์˜์›ํžˆ ๋ฉˆ์ถค (deadlock)
1
2
3
4
5
6
7
8
[thread1]                     [thread2]
mtx_a.lock() โœ“
                              mtx_b.lock() โœ“
mtx_b.lock() โœ— (waiting)
                              mtx_a.lock() โœ— (waiting)

                  ์ˆœํ™˜ ๋Œ€๊ธฐ ๊ทธ๋ž˜ํ”„:
                  thread1 โ†’ mtx_b โ†’ thread2 โ†’ mtx_a โ†’ thread1

9.3 ํšŒํ”ผ ์ „๋žต โ€” 4์กฐ๊ฑด ๊นจ๊ธฐ

9.3.1 Lock Ordering (์ˆœํ™˜ ๋Œ€๊ธฐ ๊นจ๊ธฐ)

๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ฝ์„ ์žก์œผ๋ฉด ์ˆœํ™˜์ด ์•ˆ ์ƒ๊น€.

1
2
3
4
5
6
7
8
9
10
11
// ์•ฝ์†: ํ•ญ์ƒ mtx_a โ†’ mtx_b ์ˆœ์„œ
void thread1() {
    mtx_a.lock();
    mtx_b.lock();
    ...
}
void thread2() {
    mtx_a.lock();   // โ† thread2๋„ mtx_a ๋จผ์ €
    mtx_b.lock();
    ...
}

์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ํŒจํ„ด. ๋ฝ์— ID(์ฃผ์†Œ)๋ฅผ ๋ถ€์—ฌํ•˜๊ณ  ID ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ž ๊ธˆ.

1
2
3
4
5
6
7
8
9
10
void Transfer(Account& a, Account& b, int amount) {
    Account* first = (&a < &b) ? &a : &b;
    Account* second = (&a < &b) ? &b : &a;
    first->mtx.lock();
    second->mtx.lock();
    a.balance -= amount;
    b.balance += amount;
    second->mtx.unlock();
    first->mtx.unlock();
}

9.3.2 std::scoped_lock / std::lock (Hold and Wait ๊นจ๊ธฐ)

C++17์˜ std::scoped_lock์€ ์—ฌ๋Ÿฌ ๋ฝ์„ deadlock-free ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋™์‹œ ์ž ๊ธˆ.

1
2
3
4
5
void Transfer(Account& a, Account& b, int amount) {
    std::scoped_lock lock(a.mtx, b.mtx);   // ๋‚ด๋ถ€์ ์œผ๋กœ try-lock + back-off
    a.balance -= amount;
    b.balance += amount;
}

๋‚ด๋ถ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋ณดํ†ต try-lock all then back off:

1
2
3
4
1. mtx_a.lock()
2. mtx_b.try_lock() ์‹œ๋„
   ์„ฑ๊ณต โ†’ ๋
   ์‹คํŒจ โ†’ mtx_a.unlock() (back off), ์ž ์‹œ sleep, ๋‹ค์‹œ ์‹œ๋„

์ด ๋ฐฉ์‹์€ ์ˆœ์„œ๋ฅผ ๊ณ ์ •ํ•˜์ง€ ์•Š์•„๋„ deadlock-free.

9.3.3 Timeout (No Preemption ๋ถ€๋ถ„ ๊นจ๊ธฐ)

1
2
3
4
5
6
if (mtx.try_lock_for(std::chrono::milliseconds(100))) {
    // ... ๋ฝ ์„ฑ๊ณต
    mtx.unlock();
} else {
    // ํƒ€์ž„์•„์›ƒ โ€” ๋‹ค๋ฅธ ์ฒ˜๋ฆฌ
}

ํƒ€์ž„์•„์›ƒ์ด ๋ฝ์„ ํ’€๊ฒŒ ๋งŒ๋“ค์–ด ์˜๊ตฌ ๋Œ€๊ธฐ ํšŒํ”ผ. ๋‹จ์ ์€ timeout ํ›„ ์–ด๋–ป๊ฒŒ ๋ณต๊ตฌํ• ์ง€ ์„ค๊ณ„ ๋ถ€๋‹ด.

9.3.4 Lock-free (Mutual Exclusion ์ž์ฒด ํšŒํ”ผ)

๋ฝ ์ž์ฒด๊ฐ€ ์—†์œผ๋ฉด deadlock ์—†์Œ. ๋‹จ lock-free ์ž๋ฃŒ๊ตฌ์กฐ ์„ค๊ณ„ ๋น„์šฉ ํผ.

9.4 Deadlock ๊ฐ์ง€

๊ฒ€์ถœ ๋„๊ตฌ:

  • Visual Studio Concurrency Visualizer
  • Intel Inspector
  • ThreadSanitizer (TSan, Clang/GCC) โ€” ๋™์  ๋ถ„์„
  • Helgrind (Valgrind) โ€” Linux

์ด๋“ค์€ ์ž ๊ธˆ ์ˆœ์„œ๋ฅผ ์ถ”์ ํ•ด์„œ ์ˆœํ™˜ ๋Œ€๊ธฐ ๊ฐ€๋Šฅ์„ฑ์„ ๊ฒฝ๊ณ ํ•ฉ๋‹ˆ๋‹ค.


10. LivelockยทStarvation โ€” Deadlock์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ •์ฒด

10.1 Livelock

๋ฝ์€ ์•ˆ ์žก๊ณ  ์žˆ์ง€๋งŒ ์ง„ํ–‰ ์•ˆ ๋จ. ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ์„œ๋กœ ์–‘๋ณดํ•˜๋‹ค๊ฐ€ ์˜์›ํžˆ reset.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ์˜ˆ: ์ข์€ ๋ณต๋„์—์„œ ๋‘ ์‚ฌ๋žŒ์ด ๋งˆ์ฃผ์ณ์„œ ์„œ๋กœ ๊ฐ™์€ ๋ฐฉํ–ฅ์œผ๋กœ ๋น„ํ‚ค๋Š” ์ƒํ™ฉ
bool tryAcquireBoth() {
    if (!mtx_a.try_lock()) return false;
    if (!mtx_b.try_lock()) {
        mtx_a.unlock();   // back off
        return false;
    }
    return true;
}

void worker() {
    while (true) {
        if (tryAcquireBoth()) {
            // ... ์ž‘์—…
            mtx_a.unlock();
            mtx_b.unlock();
            return;
        }
        // ์‹คํŒจ โ€” ์ฆ‰์‹œ ์žฌ์‹œ๋„
    }
}

๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์‹œ๋„ โ†’ ๋‘˜ ๋‹ค ์ฒซ ๋ฝ์€ ์žก์ง€๋งŒ ๋‘˜ ๋‹ค ๋‘ ๋ฒˆ์งธ ๋ฝ ์‹คํŒจ โ†’ ๋‘˜ ๋‹ค back off โ†’ ๋‹ค์‹œ ๋™์‹œ์— ์‹œ๋„โ€ฆ ๋ฌดํ•œ ๋ฐ˜๋ณต.

ํ•ด๊ฒฐ: random back-off. ์‹คํŒจ ์‹œ ๋ฌด์ž‘์œ„ ์‹œ๊ฐ„ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„.

1
2
3
4
if (!tryAcquireBoth()) {
    int delay = rand() % 100;
    std::this_thread::sleep_for(std::chrono::microseconds(delay));
}

Ethernet์˜ CSMA/CD๊ฐ€ ๊ฐ™์€ ์›๋ฆฌ(binary exponential backoff).

10.2 Starvation (๊ธฐ์•„)

ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ์˜์›ํžˆ ์ž์›์„ ๋ชป ์žก์Œ. deadlock๊ณผ ๋‹ฌ๋ฆฌ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ์ง„ํ–‰ ์ค‘.

์›์ธ:

  • ์šฐ์„ ์ˆœ์œ„ ์Šค์ผ€์ค„๋ง โ€” ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณ„์† ๋“ค์–ด์™€ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋Š” ๋ชป ํ•จ
  • SRWLock์˜ writer starvation โ€” reader๊ฐ€ ๊ณ„์† ๋“ค์–ด์˜ค๋ฉด writer๊ฐ€ ๋Œ€๊ธฐ์—ด์—์„œ ๋ชป ๋น ์ง
  • ๋ถˆ๊ณต์ • ๋ฝ(unfair lock) โ€” ๋ฝ release ์‹œ ์ž„์˜์˜ ๋Œ€๊ธฐ์ž ์„ ํƒ, ๊ฐ™์€ ์Šค๋ ˆ๋“œ๋งŒ ๊ณ„์† ์žก์Œ

ํ•ด๊ฒฐ: ๊ณต์ •์„ฑ(fairness) ์ •์ฑ….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ๊ณต์ • ํ โ€” FIFO
class FairMutex {
    std::mutex mtx;
    std::condition_variable cv;
    std::queue<std::thread::id> waiters;
    bool locked = false;
public:
    void lock() {
        std::unique_lock<std::mutex> ul(mtx);
        auto id = std::this_thread::get_id();
        waiters.push(id);
        cv.wait(ul, [&]{ return !locked && waiters.front() == id; });
        locked = true;
        waiters.pop();
    }
    void unlock() {
        std::unique_lock<std::mutex> ul(mtx);
        locked = false;
        cv.notify_all();
    }
};

std::mutex๋Š” ํ‘œ์ค€์ด ๊ณต์ •์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ โ€” ๊ตฌํ˜„์— ๋”ฐ๋ผ ๋‹ค๋ฆ„. ๊ณต์ •์„ฑ์ด ํ•„์š”ํ•˜๋ฉด ์ง์ ‘ ๊ตฌํ˜„ ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ.


11. Priority Inversion โ€” Pathfinder ํ™”์„ฑ ํƒ์‚ฌ์„  ์‚ฌ๋ก€

11.1 ์‹œ๋‚˜๋ฆฌ์˜ค

์„ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž:

  • H (High priority): ์ค‘์š”ํ•œ ์ž‘์—…
  • M (Medium priority): ๋ณดํ†ต ์ž‘์—…
  • L (Low priority): ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…

L์ด mutex๋ฅผ ์žก๊ณ  ์žˆ๋Š”๋ฐ H๊ฐ€ ๊ทธ mutex๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํ™ฉ:

1
2
3
4
5
6
7
8
9
์‹œ๊ฐ„ โ†’
L: โ”€โ”€[๋ฝ ์žก๊ณ  ์ž‘์—…]โ”€โ”€...
H:        โ”€โ”€[๋ฝ ๋Œ€๊ธฐ]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
M:                  โ”€โ”€[CPU ์ฐจ์ง€, ๊ณ„์† ์‹คํ–‰]โ”€โ”€
L:                  ๋ฉˆ์ถค (M์— ์˜ํ•ด ์„ ์ )
H:                  ๊ณ„์† ๋Œ€๊ธฐ (L์ด ๋ฝ ์•ˆ ํ’€์–ด์คŒ)
                    โ†‘
                    M์ด ๋๋‚  ๋•Œ๊นŒ์ง€ H๋Š” ๋ชป ์ง„ํ–‰
                    ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ L < ์ค‘๊ฐ„ M ๋•Œ๋ฌธ์— ๋†’์€ ์šฐ์„ ์ˆœ์œ„ H๊ฐ€ ์ •์ฒด

๋ฌธ์ œ: H๊ฐ€ L๋ณด๋‹ค ๋†’์€ ์šฐ์„ ์ˆœ์œ„์ธ๋ฐ๋„ M ๋•Œ๋ฌธ์— ์ง„ํ–‰ ๋ชป ํ•จ.

11.2 Mars Pathfinder ์‚ฌ๋ก€ (1997)

NASA์˜ ํ™”์„ฑ ํƒ์‚ฌ์„  Pathfinder๊ฐ€ ํ™”์„ฑ ๋„์ฐฉ ํ›„ ๋ช‡ ์‹œ๊ฐ„๋งˆ๋‹ค ์ž์ฒด ์žฌ๋ถ€ํŒ…ํ•˜๋Š” ๋ฒ„๊ทธ ๋ฐœ์ƒ. ๋ถ„์„ ๊ฒฐ๊ณผ:

  • bus management (high priority): ํ†ต์‹  ๋ฒ„์Šค ๊ด€๋ฆฌ
  • meteorological data gathering (low priority): ๊ธฐ์ƒ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
  • communication (medium priority): ์ง€๊ตฌ์™€ ํ†ต์‹ 

๊ธฐ์ƒ ๋ฐ์ดํ„ฐ ์Šค๋ ˆ๋“œ๊ฐ€ IPC ํ์˜ mutex๋ฅผ ์žก์€ ์ฑ„๋กœ ํ†ต์‹  ์Šค๋ ˆ๋“œ(medium)์— ์„ ์ ๋จ. ๊ทธ ์‚ฌ์ด bus management(high)๊ฐ€ IPC mutex๋ฅผ ๊ธฐ๋‹ค๋ฆผ. Watchdog timer๊ฐ€ bus management ๋ฉˆ์ถค์„ ๊ฐ์ง€ํ•˜๋ฉด ์‹œ์Šคํ…œ ์žฌ๋ถ€ํŒ…. ๊ฐ™์€ ์ผ์ด ๋ฐ˜๋ณต.

ํ•ด๊ฒฐ: Priority Inheritance ํ™œ์„ฑํ™” (VxWorks OS์˜ ๊ธฐ๋Šฅ์„ ์›๊ฒฉ์œผ๋กœ ์ผฌ). ์ดํ›„ ์ •์ƒ ๋™์ž‘.

11.3 Priority Inheritance (PI)

๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ์˜ ๋ฝ์„ ์žก๊ณ  ์žˆ์„ ๋•Œ, ์ผ์‹œ์ ์œผ๋กœ ๊ทธ ์Šค๋ ˆ๋“œ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ƒ์†.

1
2
3
4
5
6
7
L: โ”€โ”€[๋ฝ ์žก์Œ, ์šฐ์„ ์ˆœ์œ„ = L]โ”€โ”€
H:    โ”€โ”€[๋ฝ ๋Œ€๊ธฐ]โ”€โ”€
                โ†‘
L์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ H๋กœ ๋Œ์–ด์˜ฌ๋ฆผ (PI)
L: โ”€โ”€[๋ฝ ์žก์Œ, ์šฐ์„ ์ˆœ์œ„ = H]โ”€โ”€ (M์ด ์„ ์  ๋ชป ํ•จ)
H:                       โ”€โ”€[๋ฝ ๋ฐ›์Œ, ์šฐ์„ ์ˆœ์œ„ = H๋กœ ๋ณต๊ท€]โ”€โ”€
L: ์šฐ์„ ์ˆœ์œ„ = L๋กœ ๋ณต๊ท€

POSIX๋Š” pthread_mutexattr_setprotocol(PTHREAD_PRIO_INHERIT) ๋กœ ํ™œ์„ฑํ™”. Windows์˜ std::mutex๋Š” ๊ธฐ๋ณธ PI ์ง€์› ์—†์Œ.

11.4 Priority Ceiling Protocol (PCP)

PI์˜ ๋Œ€์•ˆ. ๊ฐ ์ž์›์— ceiling priority(๊ทธ ์ž์›์„ ์žก์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„)๋ฅผ ๋ถ€์—ฌ. ๋ฝ ์žก๋Š” ์ˆœ๊ฐ„ ๊ทธ ์Šค๋ ˆ๋“œ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ceiling์œผ๋กœ ์ƒ์Šน. ๋ฝ ํ•ด์ œ ์‹œ ์›๋ž˜๋Œ€๋กœ.

PI๋Š” ๋™์ , PCP๋Š” ์ •์ . ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ์—์„œ PCP ์„ ํ˜ธ โ€” ๋ถ„์„์ด ๋” ์‰ฌ์›€.

11.5 ๋ฉด์ ‘์—์„œ ๊ฐ•์กฐ ํฌ์ธํŠธ

  • โ€œPriority Inversion์€ OS ์Šค์ผ€์ค„๋Ÿฌ์™€ ๋ฝ์ด ํ•จ๊ป˜ ๋งŒ๋“œ๋Š” ๋ณ‘๋ฆฌโ€
  • โ€œPathfinder ์‚ฌ๋ก€๋Š” ๋™์‹œ์„ฑ ๋ฒ„๊ทธ๊ฐ€ ์šฐ์ฃผ ๋ฏธ์…˜๊นŒ์ง€ ๊นจ๋œจ๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ตํ›ˆโ€
  • โ€œ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ(์ฐจ๋Ÿ‰ ์ œ์–ด, ์˜๋ฃŒ ๊ธฐ๊ธฐ)์—์„  PI/PCP ํ•„์ˆ˜โ€

12. Windows API vs POSIX API ๋น„๊ต

12.1 Mutex

ํ•ญ๋ชฉWindowsPOSIX
์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ CRITICAL_SECTION(์—†์Œ โ€” pthread_mutex๊ฐ€ ๊ธฐ๋ณธ ๋น ๋ฆ„)
์ปค๋„ (ํ”„๋กœ์„ธ์Šค ๊ฐ„)Mutex (CreateMutex)pthread_mutex_t + PTHREAD_PROCESS_SHARED ์†์„ฑ
Reader/WriterSRWLOCK (Vista+)pthread_rwlock_t
์žฌ์ง„์ž…CRITICAL_SECTION์€ ๊ธฐ๋ณธ ์žฌ์ง„์ž…PTHREAD_MUTEX_RECURSIVE ์†์„ฑ
Robust(์ง์ ‘ ์ง€์› ์—†์Œ)PTHREAD_MUTEX_ROBUST (์†Œ์œ  ์Šค๋ ˆ๋“œ ์ฃฝ์œผ๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ํšŒ์ˆ˜ ๊ฐ€๋Šฅ)

12.2 Semaphore / Event / Condition Variable

ํ•ญ๋ชฉWindowsPOSIX
SemaphoreCreateSemaphore (์ปค๋„)sem_t (named/unnamed)
EventCreateEvent(์ง์ ‘ ๋Œ€์‘ ์—†์Œ โ€” condition variable๋กœ ํ‰๋‚ด)
Condition VariableCONDITION_VARIABLE + SleepConditionVariable*pthread_cond_t + pthread_cond_wait/signal/broadcast
Wait APIWaitForSingleObject / WaitForMultipleObjectssem_wait / pthread_cond_wait / poll ๋“ฑ ๊ฐ์ฒด๋ณ„

Windows์˜ WaitForMultipleObjects๋Š” ๋งค์šฐ ๊ฐ•๋ ฅ โ€” ์—ฌ๋Ÿฌ ํ•ธ๋“ค์„ ํ•œ ๋ฒˆ์— ๋Œ€๊ธฐ. POSIX๋Š” ๋ณดํ†ต select/poll/epoll์„ fd ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ.

12.3 Atomic

ํ•ญ๋ชฉWindowsPOSIX / GCC
์ธํฌ๋ฆฌ๋จผํŠธInterlockedIncrement__atomic_add_fetch / __sync_fetch_and_add
CASInterlockedCompareExchange__atomic_compare_exchange_n
Memory BarrierMemoryBarrier()__atomic_thread_fence(__ATOMIC_SEQ_CST)
C++ ํ‘œ์ค€๋‘˜ ๋‹ค std::atomic<T> (C++11)๋‘˜ ๋‹ค std::atomic<T>

C++11 ์ดํ›„๋กœ๋Š” ๋‘˜ ๋‹ค std::atomic<T>๋ฅผ ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค โ€” ํ”Œ๋žซํผ ์ฐจ์ด๋ฅผ ํ‘œ์ค€์ด ํก์ˆ˜.

12.4 ๋™๊ธฐํ™” ๋น„๊ต ํ‘œ

๋ฉ”์ปค๋‹ˆ์ฆ˜WindowsPOSIXC++ ํ‘œ์ค€
์‚ฌ์šฉ์ž ๋ชจ๋“œ mutexCRITICAL_SECTIONpthread_mutex_t (๊ธฐ๋ณธ)std::mutex
R/W lockSRWLOCKpthread_rwlock_tstd::shared_mutex (C++17)
์žฌ๊ท€ mutexCRITICAL_SECTION (๊ธฐ๋ณธ ์žฌ๊ท€)PTHREAD_MUTEX_RECURSIVEstd::recursive_mutex
SemaphoreCreateSemaphoresem_tstd::counting_semaphore (C++20)
Condition VariableCONDITION_VARIABLEpthread_cond_tstd::condition_variable
AtomicInterlocked*__atomic_*std::atomic<T>
Memory fenceMemoryBarrier()__atomic_thread_fencestd::atomic_thread_fence
Thread createCreateThreadpthread_createstd::thread

13. ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ ์ •๋ฆฌ โ€” ์–ด๋А ๋™๊ธฐํ™”๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋น„์‹ผ๊ฐ€

์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์—์„œ ๋™๊ธฐํ™” ๊ฐ์ฒด ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ์„ ์ผ๋ถ€ ๋‹ค๋ค˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„  race ๋ณดํ˜ธ ๊ด€์ ์—์„œ ๋‹ค์‹œ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

13.1 ๋น„์šฉ ํ‘œ

๋ฉ”์ปค๋‹ˆ์ฆ˜๋น„์šฉ (๊ฒฝํ•ฉ ์—†์Œ)๋น„์šฉ (๊ฒฝํ•ฉ)๋น„๊ณ 
std::atomic<T> load/store1~์ˆ˜ ns1~์ˆ˜ ns์บ์‹œ ๋ผ์ธ ๊ฒฝํ•ฉ ์‹œ ๋‹ค์†Œ ์ฆ๊ฐ€
atomic.fetch_add / CAS5~20 ns5~์ˆ˜์‹ญ nsLOCK prefix
CRITICAL_SECTION / std::mutex์ˆ˜์‹ญ ns1~3 ฮผs๊ฒฝํ•ฉ ์‹œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)
SRWLOCK ์ฝ๊ธฐ์ˆ˜์‹ญ ns์ˆ˜๋ฐฑ nsreader๋ผ๋ฆฌ๋Š” ๋น ๋ฆ„
SRWLOCK ์“ฐ๊ธฐ์ˆ˜์‹ญ ns1~3 ฮผsreader ๋ชจ๋‘ ๋– ๋‚  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
Mutex ์ปค๋„ ๊ฐ์ฒด1~3 ฮผs1~5 ฮผsํ•ญ์ƒ ์‹œ์Šคํ…œ ์ฝœ
Semaphore ์ปค๋„1~3 ฮผs1~5 ฮผs๋™์ผ
Event ์‹ ํ˜ธ+wait1~3 ฮผs1~5 ฮผs๊นจ์šฐ๊ธฐ๊นŒ์ง€ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜
Condition Variable wait+notify1~3 ฮผs1~5 ฮผsmutex์™€ ํ•จ๊ป˜

13.2 ๋น„์šฉ ๊ตฌ์„ฑ ๋ถ„์„

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
std::atomic CAS (์ˆ˜ ns):
  โ”” CPU LOCK prefix + ์บ์‹œ ๋ผ์ธ ๋ฝ

CRITICAL_SECTION ๊ฒฝํ•ฉ ์—†์Œ (์ˆ˜์‹ญ ns):
  โ”œ atomic CAS๋กœ ๋ฝ ์žก๊ธฐ
  โ”” ๋ฝ ํ’€๊ธฐ

CRITICAL_SECTION ๊ฒฝํ•ฉ (1~3 ฮผs):
  โ”œ spin ์‹œ๋„ (์ง€์ •๋œ spin count)
  โ”œ ์‹คํŒจ ์‹œ ์ปค๋„ ์ง„์ž… (๋ชจ๋“œ ์Šค์œ„์น˜, ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21))
  โ”œ ๋Œ€๊ธฐ ํ์— ์ถ”๊ฐ€
  โ”œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ ๊นจ์–ด๋‚จ (์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜)
  โ”” ์ž ๊ธˆ ํš๋“

Mutex ์ปค๋„ ๊ฐ์ฒด (1~5 ฮผs):
  โ”œ ์‹œ์Šคํ…œ ์ฝœ ์ง„์ž…
  โ”œ ์ปค๋„ ๊ฐ์ฒด ์ƒํƒœ ํ™•์ธ
  โ”œ ์ž ๊ธˆ ๋˜๋Š” ๋Œ€๊ธฐ (๋Œ€๊ธฐ ์‹œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜)
  โ”” ์‹œ์Šคํ…œ ์ฝœ ๋ฆฌํ„ด

13.3 ๊ฒฝํ•ฉ๋ฅ ์— ๋”ฐ๋ฅธ ์„ ํƒ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
๊ฒฝํ•ฉ ๊ฑฐ์˜ ์—†์Œ (< 1%):
  โ†’ std::atomic ๋˜๋Š” ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฝ (CRITICAL_SECTION, std::mutex)
  โ†’ ๋น„์šฉ ๊ฑฐ์˜ 0

๊ฒฝํ•ฉ ๋ณดํ†ต (1~10%):
  โ†’ ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฝ + spin (CRITICAL_SECTION์˜ spin count)
  โ†’ ์งง์€ critical section์€ spin์ด ํšจ๊ณผ์ 

๊ฒฝํ•ฉ ์‹ฌํ•จ (> 10%):
  โ†’ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์žฌ๊ฒ€ํ†  (๋ฝ ์„ธ๋ถ„ํ™”, lock striping)
  โ†’ critical section ํฌ๊ธฐ ์ค„์ด๊ธฐ
  โ†’ lock-free ์ž๋ฃŒ๊ตฌ์กฐ ๊ณ ๋ ค

๋งค์šฐ ์งง์€ critical section + ๋ฉ€ํ‹ฐ์ฝ”์–ด:
  โ†’ spin lock (atomic CAS ๊ธฐ๋ฐ˜)
  โ†’ ๋˜๋Š” lock-free

13.4 ๋ฝ ์„ธ๋ถ„ํ™” (Lock Striping)

ํฐ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ฝ์œผ๋กœ ๋‚˜๋ˆ  ๊ฒฝํ•ฉ ๋ถ„์‚ฐ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ๋‚˜์œ ์˜ˆ โ€” ํ•˜๋‚˜์˜ ๋ฝ
class HashMap {
    std::mutex mtx;
    std::vector<Bucket> buckets;
public:
    void Insert(K k, V v) {
        std::lock_guard lock(mtx);
        buckets[hash(k) % buckets.size()].insert(k, v);
    }
};

// ์ข‹์€ ์˜ˆ โ€” ๋ฒ„ํ‚ท๋ณ„ ๋ฝ
class HashMap {
    static constexpr size_t N = 16;
    std::array<std::mutex, N> mtxs;
    std::array<Bucket, N> buckets;
public:
    void Insert(K k, V v) {
        size_t i = hash(k) % N;
        std::lock_guard lock(mtxs[i]);   // โ† 1/N๋กœ ๊ฒฝํ•ฉ ๋ถ„์‚ฐ
        buckets[i].insert(k, v);
    }
};

Java์˜ ConcurrentHashMap์ด lock striping์˜ ๋Œ€ํ‘œ ์˜ˆ. ๋ถ€ํ•˜๊ฐ€ N๋ฐฐ๋กœ ๋ถ„์‚ฐ.


14. ์–ธ๋ฆฌ์–ผ์—์„œ์˜ Race Condition ํšŒํ”ผ โ€” ์Šค๋ ˆ๋“œ ๋ถ„๋ฆฌยทTaskGraph

14.1 ์–ธ๋ฆฌ์–ผ์˜ ์ฒ ํ•™ โ€” ๊ณต์œ  ์ž์ฒด๋ฅผ ์ค„์ธ๋‹ค

๋‹ค๋ฅธ ๊ฒŒ์ž„ ์—”์ง„๋“ค์ด โ€œ๋ฝ์„ ์ž˜ ๊ฑฐ๋Š”โ€ ๋ฐฉํ–ฅ์ด๋ผ๋ฉด, ์–ธ๋ฆฌ์–ผ์€ โ€œ๊ณต์œ ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”โ€ ๋ฐฉํ–ฅ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ ์Šค๋ ˆ๋“œ๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ทธ ์‚ฌ์ด๋ฅผ ๋ช…๋ น ํ๋กœ ์—ฐ๊ฒฐ.

1
2
3
4
5
6
7
8
9
10
11
12
GameThread     โ† ๊ฒŒ์ž„ ๋กœ์ง, UObject, AActor, Tick
   โ”‚
   โ”‚ ENQUEUE_RENDER_COMMAND (๋žŒ๋‹ค + ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ)
   โ†“
RenderThread   โ† ๋ Œ๋”๋ง ๋ช…๋ น ์ƒ์„ฑ, scene proxy ์‚ฌ์šฉ
   โ”‚
   โ”‚ RHI commands
   โ†“
RHIThread      โ† GPU ๋“œ๋ผ์ด๋ฒ„ ํ˜ธ์ถœ (D3D12/Vulkan/Metal)
   โ”‚
   โ†“
GPU

GameThread๋งŒ UObject๋ฅผ ๋งŒ์ง‘๋‹ˆ๋‹ค. RenderThread๋Š” GameThread๊ฐ€ ํ•œ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ •๋ฆฌํ•ด ๋„˜๊ธด scene proxy(๋ถˆ๋ณ€ ์Šค๋ƒ…์ƒท)๋งŒ ์‚ฌ์šฉ. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ UObject๋ฅผ ๋™์‹œ์— ๋งŒ์ง€๋Š” race๋Š” ๊ตฌ์กฐ์ ์œผ๋กœ ์ฐจ๋‹จ.

14.2 ENQUEUE_RENDER_COMMAND โ€” ๋ช…๋ น ํ๋กœ ๋ฐ์ดํ„ฐ ๋„˜๊ธฐ๊ธฐ

1
2
3
4
5
6
7
8
9
10
11
12
13
// GameThread ์ฝ”๋“œ
void AMyActor::UpdateMaterial(FLinearColor NewColor)
{
    FMaterialProxy* MaterialProxy = GetMaterialProxy();   // GameThread๊ฐ€ ๋งŒ๋“  proxy
    ENQUEUE_RENDER_COMMAND(UpdateColor)
    (
        [MaterialProxy, NewColor](FRHICommandListImmediate& RHICmdList)
        {
            // RenderThread์—์„œ ์‹คํ–‰
            MaterialProxy->SetColor(NewColor);
        }
    );
}

๋žŒ๋‹ค๊ฐ€ ์บก์ฒ˜ํ•œ ๊ฐ’(MaterialProxy, NewColor)์ด ๋ช…๋ น ํ์— ๋ณต์‚ฌ๋˜์–ด RenderThread๋กœ ์ „๋‹ฌ. GameThread๋Š” ์ฆ‰์‹œ ๋ฆฌํ„ด.

ํ•ต์‹ฌ โ€” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ด ๋„˜๊น€์œผ๋กœ์จ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŒ์งˆ ์ผ์ด ์—†๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ณต์œ  ์ž์ฒด๋ฅผ ํšŒํ”ผ.

14.3 TaskGraph โ€” ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋กœ ๋™์‹œ์„ฑ

ํฐ ์ž‘์—…์„ ์ž‘์€ task๋กœ ์ชผ๊ฐœ๊ณ , ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค์–ด task๋“ค์„ ๋ณ‘๋ ฌ ์‹คํ–‰.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FGraphEventRef Task1 = FFunctionGraphTask::CreateAndDispatchWhenReady(
    []() { /* ๋…๋ฆฝ ์ž‘์—… 1 */ },
    TStatId(),
    nullptr,
    ENamedThreads::AnyThread
);

FGraphEventRef Task2 = FFunctionGraphTask::CreateAndDispatchWhenReady(
    []() { /* ๋…๋ฆฝ ์ž‘์—… 2 */ },
    TStatId(),
    nullptr,
    ENamedThreads::AnyThread
);

// Task3๋Š” Task1, Task2๊ฐ€ ๋๋‚˜์•ผ ์‹œ์ž‘
FGraphEventArray Prereqs;
Prereqs.Add(Task1); Prereqs.Add(Task2);
FGraphEventRef Task3 = FFunctionGraphTask::CreateAndDispatchWhenReady(
    []() { /* ์˜์กด ์ž‘์—… */ },
    TStatId(),
    &Prereqs,
    ENamedThreads::GameThread   // ๊ฒฐ๊ณผ ํ†ตํ•ฉ์€ GameThread์—์„œ
);

๊ฐ task๊ฐ€ ๋…๋ฆฝ์ ์ด๊ฑฐ๋‚˜ ์˜์กด์„ฑ์ด ๋ช…์‹œ์ ์ด๋ผ task ์•ˆ์—์„œ๋Š” ๋ฝ์ด ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ ํ†ตํ•ฉ์€ GameThread์—์„œ ํ•˜๋ฏ€๋กœ race ์ฐจ๋‹จ.

14.4 FCriticalSection / FScopeLock โ€” ํ•„์š”ํ•  ๋•Œ๋งŒ

1
2
3
4
5
6
7
8
9
10
11
12
13
class FMyManager {
    TArray<FMyData> Data;
    FCriticalSection DataCS;
public:
    void Add(const FMyData& Item) {
        FScopeLock Lock(&DataCS);
        Data.Add(Item);
    }
    FMyData Get(int Index) {
        FScopeLock Lock(&DataCS);
        return Data[Index];   // ๋ณต์‚ฌ ๋ฐ˜ํ™˜ โ€” ๋ฝ ํ’€๋ฆฐ ๋’ค์—” ์•ˆ์ „
    }
};

FCriticalSection์€ ๋‚ด๋ถ€์ ์œผ๋กœ Windows CRITICAL_SECTION ๋˜๋Š” POSIX pthread_mutex_t. FScopeLock์€ std::lock_guard ๋™๋“ฑ.

์–ธ๋ฆฌ์–ผ ์ฝ”๋“œ ์ „์ฒด๋ฅผ grepํ•ด๋ณด๋ฉด ๋ฝ ์‚ฌ์šฉ์ด ๋†€๋ผ์šธ ์ •๋„๋กœ ์ ์Šต๋‹ˆ๋‹ค โ€” ๋ณดํ†ต ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜ธ์ถœ์ด๋‚˜ ๋น„๋™๊ธฐ I/O ๊ฒฐ๊ณผ ํ†ตํ•ฉ์—๋งŒ ๋“ฑ์žฅ.

14.5 FThreadSafeCounter โ€” atomic ์นด์šดํ„ฐ

1
2
3
4
5
6
class FMyClass {
    FThreadSafeCounter Counter;   // std::atomic<int32> ๋™๋“ฑ
public:
    int32 Increment() { return Counter.Increment(); }
    int32 GetValue() const { return Counter.GetValue(); }
};

๋‚ด๋ถ€๋Š” InterlockedIncrement. std::atomic<int32>์™€ ๊ฑฐ์˜ ๊ฐ™์ง€๋งŒ ์–ธ๋ฆฌ์–ผ์˜ reflectionยทserialization ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ์ž์ฒด ํƒ€์ž… ์ œ๊ณต.

14.6 TQueue<T, EQueueMode::Spsc> โ€” Lock-free ํ

1
2
3
4
5
6
7
8
9
10
11
TQueue<FCommand, EQueueMode::Spsc> CommandQueue;
// Single Producer, Single Consumer

// Producer (GameThread)
CommandQueue.Enqueue(FCommand{ ... });

// Consumer (RenderThread)
FCommand Cmd;
while (CommandQueue.Dequeue(Cmd)) {
    Process(Cmd);
}

SPSC๋Š” ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ lock-free ํ โ€” producer์™€ consumer๊ฐ€ ๊ฐ๊ฐ ํ•œ ๋ช…๋ฟ์ด๋ฉด atomic head/tail๋งŒ์œผ๋กœ ์•ˆ์ „.

MPSC(Multi Producer Single Consumer)๋„ ์ง€์› โ€” ์—ฌ๋Ÿฌ ์›Œ์ปค โ†’ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๊ฒฐ๊ณผ ๋ชจ์œผ๊ธฐ ํŒจํ„ด.

14.7 ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ์ฝ”๋“œ์—์„œ ๋ณดํ†ต race ์•ˆ ๋งŒ๋‚จ

๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋ณดํ†ต ๋งˆ์ฃผ์น˜๋Š” race๋Š”:

  • AsyncTask ๊ฒฐ๊ณผ๋ฅผ GameThread๋กœ ๊ฐ€์ ธ์˜ฌ ๋•Œ โ€” AsyncTask(ENamedThreads::GameThread, ...) ์‚ฌ์šฉ
  • TaskGraph๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… โ†’ ๊ฒฐ๊ณผ ํ†ตํ•ฉ
  • Subsystem์—์„œ worker ์Šค๋ ˆ๋“œ ์‚ฌ์šฉ ์‹œ

๋Œ€๋ถ€๋ถ„์€ ์œ„ ํŒจํ„ด(๋ช…๋ น ํยทTaskGraphยทproxy ๋ณต์‚ฌ)์œผ๋กœ ํ•ด๊ฒฐ๋˜๊ณ , ์ง์ ‘ mutex๋ฅผ ๋งŒ์ ธ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค โ€” ๊ทธ๊ฒŒ ์–ธ๋ฆฌ์–ผ์˜ ๋™์‹œ์„ฑ ๋ชจ๋ธ ๊ฐ•์ .

14.8 ์–ธ๋ฆฌ์–ผ ๋™์‹œ์„ฑ ์ •๋ฆฌ ํ‘œ

์‹œ๋‚˜๋ฆฌ์˜ค๋ฉ”์ปค๋‹ˆ์ฆ˜๋น„๊ณ 
GameThread โ†” RenderThreadENQUEUE_RENDER_COMMAND + scene proxy๋ช…๋ น ํยท๋ฐ์ดํ„ฐ ๋ณต์‚ฌ
GameThread โ†” RHIThreadRenderThread ๊ฒฝ์œ ์ง์ ‘ ์•ˆ ๋งŒ์ง
๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…TaskGraph / AsyncTask์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„
๋น„๋™๊ธฐ ๊ฒฐ๊ณผ ํ†ตํ•ฉAsyncTask(ENamedThreads::GameThread)๊ฒฐ๊ณผ๋Š” GameThread์—์„œ
๊ณต์œ  ์นด์šดํ„ฐFThreadSafeCounteratomic
๊ณต์œ  ์ปจํ…Œ์ด๋„ˆFCriticalSection + FScopeLock๋“œ๋ฌผ๊ฒŒ
Producer-ConsumerTQueue<T, EQueueMode::Spsc/Mpsc>lock-free

15. ๊ผฌ๋ฆฌ์งˆ๋ฌธ ์˜ˆ์ƒ ๊ฒฝ๋กœ

Q1. โ€œRace Condition์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐ ํ•ด์ฃผ์„ธ์š”.โ€

Race Condition์€ ๋‘˜ ์ด์ƒ์˜ ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ ์ ‘๊ทผํ•  ๋•Œ, ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋น„๊ฒฐ์ •์  ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ๋ฐœ์ƒ ์กฐ๊ฑด์€ ๋„ค ๊ฐ€์ง€ โ€” ๊ณต์œ  ์ž์›์ด ์กด์žฌํ•˜๊ณ , ๋‘˜ ์ด์ƒ์ด ๋™์‹œ ์ ‘๊ทผํ•˜๋ฉฐ, ๊ทธ์ค‘ ์ ์–ด๋„ ํ•˜๋‚˜๊ฐ€ ์“ฐ๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , OS๊ฐ€ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋„ค ์กฐ๊ฑด์ด ๋ชจ๋‘ ๋งŒ์กฑ๋˜์–ด์•ผ race๊ฐ€ ์ผ์–ด๋‚˜๊ณ , ์–ด๋А ํ•˜๋‚˜๋งŒ ๊นจ๋„ race๋Š” ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ˆ๊ฐ€ count++์ž…๋‹ˆ๋‹ค. ์ด ํ•œ ์ค„์ด CPU ๋ช…๋ น์œผ๋กœ๋Š” load, increment, store ์„ธ ๋‹จ๊ณ„๋กœ ๋ถ„ํ•ด๋˜๊ณ , ๊ทธ ์‚ฌ์ด ์–ด๋””์„œ๋‚˜ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ 100์—์„œ ์‹œ์ž‘ํ•ด ๊ฐ์ž +1์„ ํ•œ ๊ฒฐ๊ณผ๊ฐ€ 102๊ฐ€ ์•„๋‹ˆ๋ผ 101์ด ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒŒ ์ด race์˜ ๋ณธ์งˆ์ž…๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์˜ ์ถ”์ƒ ๊ฐœ๋…์€ Critical Section(์ž„๊ณ„ ์˜์—ญ) โ€” ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ณดํ˜ธํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ๋™๊ธฐํ™” ๊ฐ์ฒด(mutexยทsemaphoreยทatomic ๋“ฑ)์ด๊ณ , ๋น„์šฉ ์ˆœ์„œ๋Š” atomic CAS(์ˆ˜ ns) < ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฝ(์ˆ˜์‹ญ ns~1 ฮผs) < ์ปค๋„ ๊ฐ์ฒด ๋ฝ(1~3 ฮผs) ์ˆœ์ž…๋‹ˆ๋‹ค. ๋” ๋‚˜์•„๊ฐ€ lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋ฝ ์ž์ฒด๋ฅผ ํšŒํ”ผํ•˜๊ธฐ๋„ ํ•˜์ง€๋งŒ, ABA ๋ฌธ์ œยทmemory reclamation ๊ฐ™์€ ์ถ”๊ฐ€ ํ•จ์ •์ด ์žˆ์–ด ์ง์ ‘ ๋งŒ๋“ค๊ธฐ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

Q2. โ€œCritical Section์ด ๋ฌด์—‡์ธ๊ฐ€์š”?โ€

Critical Section(์ž„๊ณ„ ์˜์—ญ)์€ ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ถ”์ƒ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. ์ž์›์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ ๊ตฌ๊ฐ„์˜ ๊ฐœ๋…์ด๋ผ๋Š” ์ ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ์ž์›์ด๋ผ๋„ ๋ณดํ˜ธ๊ฐ€ ํ•„์š”ํ•œ ๊ตฌ๊ฐ„์ด ์žˆ๊ณ  ํ•„์š” ์—†๋Š” ๊ตฌ๊ฐ„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ข‹์€ critical section์€ ๋„ค ์กฐ๊ฑด์„ ๋งŒ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค โ€” mutual exclusion(ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์ง„์ž…), progress(๋น„์–ด ์žˆ์œผ๋ฉด ๋ˆ„๊ตฐ๊ฐ€ ๋“ค์–ด๊ฐ), bounded waiting(๋ฌดํ•œ ๋Œ€๊ธฐ ์—†์Œ), no starvation(๋ชจ๋‘๊ฐ€ ๊ฒฐ๊ตญ ๋“ค์–ด๊ฐ). ์ด ๋„ค ๊ฐ€์ง€๊ฐ€ DijkstraยทHoare๊ฐ€ ์ •๋ฆฌํ•œ ๊ณ ์ „์  ์ •์˜์ž…๋‹ˆ๋‹ค.

์‹ค๋ฌด์—์„  critical section์˜ ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๊ฒŒ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค โ€” ๋ฝ ์•ˆ์—์„œ ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์„ ํ•˜๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ชจ๋‘ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ lock contention(๋ฝ ๊ฒฝํ•ฉ)์ด ํญ์ฆํ•ด ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์ด ๋ˆ„์ ๋ฉ๋‹ˆ๋‹ค. ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์€ ๋ฝ ๋ฐ–์—์„œ ํ•˜๊ณ , ๋ฝ ์•ˆ์—์„œ๋Š” ๊ณต์œ  ์ž์› ๊ฐฑ์‹ ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

Windows์—์„œ๋Š” CRITICAL_SECTION API(๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ํ•œ์ •, ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ )๊ฐ€ ๊ฐ€์žฅ ๊ฐ€๋ณ๊ณ , ์ปค๋„ Mutex(CreateMutex)๋Š” ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ  ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋” ๋น„์Œ‰๋‹ˆ๋‹ค. C++ ํ‘œ์ค€์—์„œ๋Š” std::mutex + std::lock_guard RAII ํŒจํ„ด์ด ํ‘œ์ค€์ž…๋‹ˆ๋‹ค.

Q3. โ€œstd::mutex์™€ std::atomic ์ค‘ ๋ฌด์—‡์„ ์จ์•ผ ํ•˜๋‚˜์š”?โ€

์ž๋ฃŒ๊ตฌ์กฐ์˜ ๋ณต์žก๋„์™€ ๊ฒฝํ•ฉ๋ฅ ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

std::atomic์€ ๋‹จ์ผ ๊ฐ’์— ๋Œ€ํ•œ read-modify-write๊ฐ€ ๋‹จ์ผ ๋ช…๋ น์œผ๋กœ ๋๋‚  ๋•Œ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋น„์šฉ์€ ์ˆ˜ ns๋กœ ๊ทนํžˆ ์ €๋ ดํ•ฉ๋‹ˆ๋‹ค. ์นด์šดํ„ฐ, ํ”Œ๋ž˜๊ทธ, ๋‹จ์ผ ํฌ์ธํ„ฐ(scene ๊ฐฑ์‹  ์‹ ํ˜ธ) ๊ฐ™์€ ํŒจํ„ด์ด ์ „ํ˜•์ ์ž…๋‹ˆ๋‹ค. ๋‹จ์ ์€ ๋ณตํ•ฉ ์—ฐ์‚ฐ์ด ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ โ€” count++๋Š” atomic์ด์ง€๋งŒ if (count > 10) count = 0;๋Š” atomic์ด ์•„๋‹™๋‹ˆ๋‹ค.

std::mutex๋Š” ๋ณตํ•ฉ ์—ฐ์‚ฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ๊ฐฑ์‹ ํ•˜๊ฑฐ๋‚˜, ์ž๋ฃŒ๊ตฌ์กฐ(mapยทlistยทvector)๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒฝ์šฐ. ๋น„์šฉ์€ ๊ฒฝํ•ฉ ์—†์„ ๋•Œ ์ˆ˜์‹ญ ns๋กœ atomic๋ณด๋‹ค ์•ฝ๊ฐ„ ๋น„์‹ธ์ง€๋งŒ, ๊ฒฝํ•ฉ ์‹œ 1~3 ฮผs๋กœ ๋น„์šฉ์ด ๊ธ‰์ฆํ•ฉ๋‹ˆ๋‹ค.

์„ ํƒ ๊ธฐ์ค€:

  • ๋‹จ์ผ ๊ฐ’ + ๋‹จ์ˆœ ์—ฐ์‚ฐ โ†’ atomic (counter, ref count, flag)
  • ๋ณตํ•ฉ ์—ฐ์‚ฐ ๋˜๋Š” ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜ โ†’ mutex
  • ์ฝ๊ธฐ ๋งŽ๊ณ  ์“ฐ๊ธฐ ์ ์Œ โ†’ std::shared_mutex (R/W lock)
  • ๋งค์šฐ ์งง์€ critical section + ๋ฉ€ํ‹ฐ์ฝ”์–ด โ†’ spin lock (atomic CAS๋กœ ์ง์ ‘ ๊ตฌํ˜„)
  • ๊ณ ๊ธ‰ โ€” lock-free ์ž๋ฃŒ๊ตฌ์กฐ โ†’ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Boost.Lockfree)

ํ•œ ๊ฐ€์ง€ ์ฃผ์˜ โ€” atomic์„ ๋„ˆ๋ฌด ์ž˜๊ฒŒ ์“ฐ๋ฉด ์˜คํžˆ๋ ค ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค atomic ์—ฐ์‚ฐ์ด ์บ์‹œ ์ผ๊ด€์„ฑ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐœ์ƒ์‹œํ‚ค๋ฏ€๋กœ, ์ฝ”์–ด๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๋น„์šฉ์ด ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ hot path์˜ atomic์€ ์‹ ์ค‘ํ•˜๊ฒŒ.

Q4. โ€œDeadlock์€ ์–ด๋–ป๊ฒŒ ๋ง‰์„ ์ˆ˜ ์žˆ๋‚˜์š”?โ€

Deadlock์€ Coffman 4์กฐ๊ฑด(์ƒํ˜ธ๋ฐฐ์ œยท์ ์œ ์™€ ๋Œ€๊ธฐยท๋น„์„ ์ ยท์ˆœํ™˜ ๋Œ€๊ธฐ)์ด ๋ชจ๋‘ ๋งŒ์กฑ๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋„ค ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋งŒ ๊นจ๋„ deadlock์€ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์‹ค์šฉ์ ์ธ ํšŒํ”ผ ์ „๋žต์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

์ฒซ์งธ, lock ordering(๋ฝ ์ˆœ์„œ ๊ณ ์ •) โ€” ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ฝ์„ ์žก์œผ๋ฉด ์ˆœํ™˜ ๋Œ€๊ธฐ๊ฐ€ ์•ˆ ์ƒ๊น๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ํŒจํ„ด. ๋ฝ์— ID(์˜ˆ: ๊ฐ์ฒด ์ฃผ์†Œ)๋ฅผ ๋ถ€์—ฌํ•˜๊ณ  ํ•ญ์ƒ ID ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ž ๊ธ‰๋‹ˆ๋‹ค.

๋‘˜์งธ, std::scoped_lock โ€” C++17์˜ std::scoped_lock์€ ์—ฌ๋Ÿฌ mutex๋ฅผ deadlock-free ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋™์‹œ์— ์ž ๊ธ‰๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ try-lock ์‹œ๋„ + back-off ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋ฝ ์ˆœ์„œ๋ฅผ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
void Transfer(Account& a, Account& b, int amount) {
    std::scoped_lock lock(a.mtx, b.mtx);   // deadlock-free
    a.balance -= amount;
    b.balance += amount;
}

๊ทธ ์™ธ ์ „๋žต์œผ๋กœ timeout(try_lock_for)์„ ์จ์„œ ์ผ์ • ์‹œ๊ฐ„ ํ›„ ํฌ๊ธฐ, lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋ฝ ์ž์ฒด๋ฅผ ํšŒํ”ผ, ๊ทธ๋ฆฌ๊ณ  ์ •์  ๋ถ„์„ ๋„๊ตฌ(Visual Studio Concurrency Visualizer, ThreadSanitizer, Helgrind)๋กœ deadlock ๊ฐ€๋Šฅ์„ฑ ์‚ฌ์ „ ๊ฐ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Q5. โ€œLock-free์™€ wait-free์˜ ์ฐจ์ด๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?โ€

๋‘˜ ๋‹ค ๋ฝ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋™๊ธฐํ™” ๊ธฐ๋ฒ•์ด์ง€๋งŒ ์ง„ํ–‰ ๋ณด์žฅ ๊ฐ•๋„๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

Lock-free๋Š” ์‹œ์Šคํ…œ ์ „์ฒด๋กœ ๋ดค์„ ๋•Œ ์ ์–ด๋„ ํ•œ ์Šค๋ ˆ๋“œ๋Š” finite step ์•ˆ์— ์ง„ํ–‰๋œ๋‹ค๋Š” ๋ณด์žฅ์ž…๋‹ˆ๋‹ค. ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋А๋ ค์ ธ๋„ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ง‰ํžˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰ deadlockยทlivelock์ด ๋ถˆ๊ฐ€๋Šฅ. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ณ„ ์Šค๋ ˆ๋“œ๊ฐ€ starvation ๋‹นํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.

Wait-free๋Š” ๋” ๊ฐ•ํ•œ ๋ณด์žฅ์œผ๋กœ ๋ชจ๋“  ๊ฐœ๋ณ„ ์Šค๋ ˆ๋“œ๊ฐ€ finite step ์•ˆ์— ์ง„ํ–‰๋œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ์Šค๋ ˆ๋“œ๋„ starvation ๋‹นํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์‹ค์šฉ์  lock-free ์ž๋ฃŒ๊ตฌ์กฐ๋Š” wait-free๊นŒ์ง€๋Š” ๋ชป ๊ฐ€๊ณ  lock-free์— ๋จธ๋ญ…๋‹ˆ๋‹ค โ€” wait-free๋Š” ์„ค๊ณ„๊ฐ€ ํ›จ์”ฌ ์–ด๋ ต๊ณ  ๋น„์šฉ๋„ ํฝ๋‹ˆ๋‹ค. std::atomic์˜ fetch_add ๊ฐ™์€ ๋‹จ์ผ ์—ฐ์‚ฐ์€ wait-free์ง€๋งŒ, lock-free ์Šคํƒ์˜ pop์€ CAS ๋ฃจํ”„๋ผ lock-free์ผ ๋ฟ wait-free๋Š” ์•„๋‹™๋‹ˆ๋‹ค(๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณ„์† pushํ•˜๋ฉด ํ•œ ์Šค๋ ˆ๋“œ์˜ pop์ด ๊ณ„์† ์žฌ์‹œ๋„๋  ์ˆ˜ ์žˆ์Œ).

์ง„ํ–‰ ๋ณด์žฅ ๊ฐ•๋„ ์ˆœ์„œ: blocking < obstruction-free < lock-free < wait-free.

Q6. โ€œABA ๋ฌธ์ œ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?โ€

ABA ๋ฌธ์ œ๋Š” lock-free ์ž๋ฃŒ๊ตฌ์กฐ์—์„œ CAS(Compare-And-Swap)๊ฐ€ ๋ณ€ํ™”๋ฅผ ๋†“์น˜๋Š” ํ•จ์ •์ž…๋‹ˆ๋‹ค. ๊ฐ’์ด A์—์„œ B๋กœ ๋ฐ”๋€Œ์—ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ A๋กœ ๋Œ์•„์™”์„ ๋•Œ, CAS๋Š” ๊ฐ’(์ฃผ์†Œ)๋งŒ ๋น„๊ตํ•˜๋ฏ€๋กœ โ€œ๋ณ€ํ™” ์—†์Œโ€์œผ๋กœ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ํ”ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” lock-free ์Šคํƒ์˜ pop์ž…๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ 1์ด head=A๋ฅผ ๋ณด๊ณ  CAS๋กœ head๋ฅผ A->next๋กœ ๋ฐ”๊พธ๋ ค๋Š” ์‚ฌ์ด, ์Šค๋ ˆ๋“œ 2๊ฐ€ A๋ฅผ popํ•˜๊ณ  freeํ•œ ๋’ค ์ƒˆ ๋…ธ๋“œ๋ฅผ pushํ–ˆ๋Š”๋ฐ ์šฐ์—ฐํžˆ ๊ฐ™์€ ์ฃผ์†Œ๋ฅผ ๋ฐ›์•„ head๊ฐ€ ๋‹ค์‹œ A๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ 1์˜ CAS๋Š” head==A๋กœ ๋ณด๊ณ  ์„ฑ๊ณตํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” free๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ(10)๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

ํšŒํ”ผ ๊ธฐ๋ฒ•์€ ๋„ค ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • Tagged Pointer โ€” ํฌ์ธํ„ฐ์— ๋ฒ„์ „ ์นด์šดํ„ฐ๋ฅผ ๋ถ™์—ฌ ๋งค ์ˆ˜์ •๋งˆ๋‹ค +1. x86์˜ LOCK CMPXCHG16B๋กœ double-word CAS.
  • Hazard Pointer โ€” ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ โ€œํ˜„์žฌ ๋ณด๊ณ  ์žˆ๋Š” ํฌ์ธํ„ฐโ€๋ฅผ ๋“ฑ๋ก. ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜ ์‹œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ hazard pointer๋ฅผ ๋ชจ๋‘ ๊ฒ€์‚ฌ.
  • Epoch-based Reclamation โ€” ์ „์—ญ epoch์„ ์ถ”์ ํ•ด ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ค์Œ epoch์œผ๋กœ ๋„˜์–ด๊ฐ„ ๋’ค์—๋งŒ ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜. Linux RCU์˜ ์›๋ฆฌ.
  • Garbage Collection โ€” JavaยทC#์ฒ˜๋Ÿผ GC๊ฐ€ ์žˆ๋Š” ์–ธ์–ด์—์„  ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ.

C++์€ GC๊ฐ€ ์—†์–ด ์œ„ ์„ธ ๊ฐ€์ง€๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ lock-free ์ž๋ฃŒ๊ตฌ์กฐ ์„ค๊ณ„๊ฐ€ ๋งค์šฐ ์–ด๋ ต๊ณ , ์‹ค๋ฌด์—์„œ๋Š” Boost.Lockfree๋‚˜ folly ๊ฐ™์€ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์”๋‹ˆ๋‹ค.

Q7. โ€œMemory Barrier๊ฐ€ ์™œ ํ•„์š”ํ•œ๊ฐ€์š”?โ€

ํ˜„๋Œ€ CPU์™€ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์„ฑ๋Šฅ์„ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์˜๋ฏธ๋Š” ๋ณด์กดํ•˜์ง€๋งŒ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ์—์„œ๋Š” ํ•œ ์Šค๋ ˆ๋“œ์˜ ์“ฐ๊ธฐ ์ˆœ์„œ๊ฐ€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ๋‹ค๋ฅด๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€ํ‘œ ์˜ˆ์‹œ:

1
2
3
4
5
6
// ์Šค๋ ˆ๋“œ A
data = 42;
ready = true;

// ์Šค๋ ˆ๋“œ B
if (ready) use(data);   // โ† data๊ฐ€ 42๊ฐ€ ์•„๋‹ ์ˆ˜ ์žˆ์Œ!

A์˜ ์ปดํŒŒ์ผ๋Ÿฌ๋‚˜ CPU๊ฐ€ ๋‘ ์“ฐ๊ธฐ๋ฅผ ์žฌ๋ฐฐ์น˜ํ•ด์„œ ready = true๊ฐ€ ๋จผ์ € ๋ณด์ผ ์ˆ˜ ์žˆ๊ณ , B๋Š” ready==true์ธ๋ฐ data๋Š” ์•„์ง 42๊ฐ€ ์•„๋‹Œ ์ƒํƒœ๋ฅผ ๋ด…๋‹ˆ๋‹ค.

Memory Barrier(๋˜๋Š” fence) ๋Š” ์ด ์žฌ๋ฐฐ์น˜๋ฅผ ๋ง‰๋Š” ๋ช…๋ น์ž…๋‹ˆ๋‹ค. C++์—์„œ๋Š” std::atomic์— memory_order๋ฅผ ๋ช…์‹œํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค โ€” memory_order_release๋Š” store์— ๋ถ™์—ฌ โ€œ์ด์ „ ์“ฐ๊ธฐ๊ฐ€ ๋’ค๋กœ ์•ˆ ๊ฐโ€, memory_order_acquire๋Š” load์— ๋ถ™์—ฌ โ€œ์ดํ›„ ์ฝ๊ธฐ๊ฐ€ ์•ž์œผ๋กœ ์•ˆ ์˜ดโ€์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
// ์•ˆ์ „ ๋ฒ„์ „
data = 42;
ready.store(true, std::memory_order_release);

// ์Šค๋ ˆ๋“œ B
while (!ready.load(std::memory_order_acquire));
use(data);   // 42 ๋ณด์žฅ

release-acquire ํŽ˜์–ด๊ฐ€ ๋‘ ์Šค๋ ˆ๋“œ ๊ฐ„ happens-before ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค์–ด release ์ด์ „์˜ ๋ชจ๋“  ์“ฐ๊ธฐ๊ฐ€ acquire ์ดํ›„์˜ ๋ชจ๋“  ์ฝ๊ธฐ์—์„œ ๋ณด์ž…๋‹ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ์€ ์•„ํ‚คํ…์ฒ˜๋งˆ๋‹ค ๋‹ค๋ฆ…๋‹ˆ๋‹ค โ€” x86์€ TSO๋ผ ๋น„๊ต์  ๊ฐ•ํ•œ ๋ชจ๋ธ(storeโ†’load๋งŒ ์žฌ๋ฐฐ์น˜)์ด๋ผ์„œ ๋ฌด์˜์‹์ ์œผ๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ x86์—์„  ์ž˜ ๋Œ ์ˆ˜ ์žˆ์ง€๋งŒ, ARM์€ weakํ•ด์„œ ๋ชจ๋“  ์žฌ๋ฐฐ์น˜ ํ—ˆ์šฉ โ€” ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ARM์—์„œ race๋กœ ๊นจ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Cross-platform ์ฝ”๋“œ๋Š” ๋ช…์‹œ์  memory order๊ฐ€ ํ•„์ˆ˜.

Q8. โ€œPriority Inversion์ด ๋ฌด์—‡์ธ๊ฐ€์š”?โ€

Priority Inversion์€ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์žก๊ณ  ์žˆ๋Š” ๋ฝ์„ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š”๋ฐ, ์ค‘๊ฐ„ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ CPU๋ฅผ ์ฐจ์ง€ํ•ด ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์ง„ํ–‰ ๋ชป ํ•˜๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ์ค‘๊ฐ„ ์šฐ์„ ์ˆœ์œ„์— ์˜ํ•ด ์ •์ฒด๋˜๋Š” ์—ญ์ „.

๊ฐ€์žฅ ์œ ๋ช…ํ•œ ์‚ฌ๋ก€๊ฐ€ 1997๋…„ NASA Pathfinder ํ™”์„ฑ ํƒ์‚ฌ์„ ์ž…๋‹ˆ๋‹ค. ๊ธฐ์ƒ ๋ฐ์ดํ„ฐ ์Šค๋ ˆ๋“œ(low)๊ฐ€ IPC ํ์˜ mutex๋ฅผ ์žก๊ณ  ์žˆ๋Š” ๋™์•ˆ ํ†ต์‹  ์Šค๋ ˆ๋“œ(medium)์— ์„ ์ ๋๊ณ , ๊ทธ ์‚ฌ์ด bus management ์Šค๋ ˆ๋“œ(high)๊ฐ€ IPC mutex๋ฅผ ๊ธฐ๋‹ค๋ ธ์Šต๋‹ˆ๋‹ค. Watchdog timer๊ฐ€ bus management ๋ฉˆ์ถค์„ ๊ฐ์ง€ํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ์žฌ๋ถ€ํŒ…. VxWorks OS์˜ Priority Inheritance ๊ธฐ๋Šฅ์„ ์›๊ฒฉ์œผ๋กœ ํ™œ์„ฑํ™”ํ•˜์ž ํ•ด๊ฒฐ๋์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ… ๋‘ ๊ฐ€์ง€:

  • Priority Inheritance(PI) โ€” ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์žก์€ ๋™์•ˆ, ๊ทธ ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์ผ์‹œ ์ƒ์Šน. ๋ฝ ํ•ด์ œ ์‹œ ์›๋ž˜ ์šฐ์„ ์ˆœ์œ„๋กœ ๋ณต๊ท€. POSIX๋Š” pthread_mutexattr_setprotocol(PTHREAD_PRIO_INHERIT)๋กœ ํ™œ์„ฑํ™”.
  • Priority Ceiling Protocol(PCP) โ€” ๊ฐ ์ž์›์— ์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„(ceiling)๋ฅผ ๋ฏธ๋ฆฌ ๋ถ€์—ฌ. ๋ฝ ์žก๋Š” ์ˆœ๊ฐ„ ceiling์œผ๋กœ ์ƒ์Šน. ์ •์ ์ด๊ณ  ๋ถ„์„์ด ์‰ฌ์›Œ ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ์— ์„ ํ˜ธ.

์ผ๋ฐ˜ ๋ฐ์Šคํฌํ†ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„  ๊ฑฐ์˜ ๋ณด์ด์ง€ ์•Š์ง€๋งŒ, ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ(์ฐจ๋Ÿ‰ ์ œ์–ด, ์˜๋ฃŒ ๊ธฐ๊ธฐ, ํ•ญ๊ณต์šฐ์ฃผ) ์—์„œ๋Š” PI/PCP๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

Q9. โ€œ์–ธ๋ฆฌ์–ผ์€ race condition์„ ์–ด๋–ป๊ฒŒ ํšŒํ”ผํ•˜๋‚˜์š”?โ€

์–ธ๋ฆฌ์–ผ์˜ ๋™์‹œ์„ฑ ์ฒ ํ•™์€ โ€œ๋ฝ์„ ์ž˜ ๊ฑฐ๋Š”โ€ ๊ฒŒ ์•„๋‹ˆ๋ผ โ€œ๊ณต์œ ๋ฅผ ์•ˆ ๋งŒ๋“œ๋Š”โ€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ ์Šค๋ ˆ๋“œ๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค โ€” GameThread(UObjectยทTick), RenderThread(๋ Œ๋” ๋ช…๋ น), RHIThread(GPU ๋“œ๋ผ์ด๋ฒ„ ํ˜ธ์ถœ).

ํ•ต์‹ฌ ๊ธฐ๋ฒ• ์…‹:

์ฒซ์งธ, ๋ช…๋ น ํ๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ โ€” ENQUEUE_RENDER_COMMAND ๋งคํฌ๋กœ๋Š” GameThread์—์„œ RenderThread๋กœ ๋žŒ๋‹ค๋ฅผ ๋ณด๋‚ด๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋žŒ๋‹ค๊ฐ€ ์บก์ฒ˜ํ•œ ๊ฐ’์ด ๋ช…๋ น ํ์— ๋ณต์‚ฌ๋˜๋ฏ€๋กœ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŒ์งˆ ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ฐœ์ƒ 4์กฐ๊ฑด ์ค‘ โ€œโ‘ก ๋™์‹œ ์ ‘๊ทผโ€์„ ๊ตฌ์กฐ์ ์œผ๋กœ ์ฐจ๋‹จ.

๋‘˜์งธ, scene proxy ํŒจํ„ด โ€” GameThread๋Š” UObject ์ž์ฒด๋ฅผ, RenderThread๋Š” GameThread๊ฐ€ ๋งค ํ”„๋ ˆ์ž„ ์ •๋ฆฌํ•ด ๋„˜๊ธด ๋ถˆ๋ณ€ proxy๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๋งŒ์ง€๋Š” ์ผ์ด ์›์ฒœ์ ์œผ๋กœ ์—†์Šต๋‹ˆ๋‹ค.

์…‹์งธ, TaskGraph โ€” ํฐ ์ž‘์—…์„ task๋กœ ์ชผ๊ฐœ๊ณ  ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค์–ด ๋ณ‘๋ ฌ ์‹คํ–‰. task ๋‚ด๋ถ€๋Š” ๋…๋ฆฝ์ด๊ฑฐ๋‚˜ ๋ช…์‹œ์  ์˜์กด๋งŒ ์žˆ์–ด ๋ฝ ๊ฑฐ์˜ ์—†์Œ. ๊ฒฐ๊ณผ๋Š” AsyncTask(ENamedThreads::GameThread)๋กœ GameThread์—์„œ ํ†ตํ•ฉ.

๊ทธ๋ž˜๋„ ๋ฝ์ด ํ•„์š”ํ•  ๋•Œ๋Š” FCriticalSection + FScopeLock(RAII ๊ฐ€๋“œ), atomic ์นด์šดํ„ฐ๋กœ FThreadSafeCounter, lock-free ํ๋กœ TQueue<T, EQueueMode::Spsc/Mpsc>๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์–ธ๋ฆฌ์–ผ ์ฝ”๋“œ์—์„œ ๋ฝ ์‚ฌ์šฉ ๋นˆ๋„๋Š” ๋†€๋ผ์šธ ์ •๋„๋กœ ๋‚ฎ์Šต๋‹ˆ๋‹ค โ€” ๋™์‹œ์„ฑ ๋ชจ๋ธ ์ž์ฒด๊ฐ€ race๋ฅผ ์•ˆ ๋งŒ๋“œ๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์„ค๊ณ„๋๊ธฐ ๋•Œ๋ฌธ.

Q10. โ€œSpin Lock๊ณผ Mutex ์ค‘ ๋ฌด์—‡์ด ๋” ์ข‹๋‚˜์š”?โ€

์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

Spin Lock์€ ๋ฝ์ด ํ’€๋ฆด ๋•Œ๊นŒ์ง€ busy-wait(while loop)ํ•˜๋Š” ๋ฝ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21) ์—†์ด atomic CAS๋งŒ์œผ๋กœ ๋™๊ธฐํ™”. ๋ฝ ์žกํž ๋•Œ๊นŒ์ง€ CPU๋ฅผ ์“ฐ๋ฉด์„œ ๋Œ€๊ธฐ.

Mutex๋Š” ๋ฝ์„ ๋ชป ์žก์œผ๋ฉด ์Šค๋ ˆ๋“œ๋ฅผ sleep์‹œ์ผœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— CPU๋ฅผ ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค. ๊นจ์–ด๋‚  ๋•Œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜ ๋ฐœ์ƒ.

๋น„๊ต:

์กฐ๊ฑดSpin Lock ์œ ๋ฆฌMutex ์œ ๋ฆฌ
Critical section ๊ธธ์ด๋งค์šฐ ์งง์Œ (< 1 ฮผs)๊ธธ์Œ (> 10 ฮผs)
CPU ์ฝ”์–ด ์ˆ˜๋ฉ€ํ‹ฐ์ฝ”์–ด๋‹จ์ผ ์ฝ”์–ด โ€” spin์ด ์ฃฝ์ž„
๊ฒฝํ•ฉ๋ฅ ๋‚ฎ์Œ๋†’์Œ
๋ฝ ์žกํžŒ ์Šค๋ ˆ๋“œ๊ฐ€ ์ง„ํ–‰ ๊ฐ€๋Šฅ?YES (๋‹ค๋ฅธ ์ฝ”์–ด)(sleep์€ ์–ด์ฐจํ”ผ ์–‘๋ณด)

๋‹จ์ผ ์ฝ”์–ด์—์„œ spin lock์€ ๊ฑฐ์˜ ํ•ญ์ƒ ์†ํ•ด์ž…๋‹ˆ๋‹ค โ€” ๋ฝ์„ ์žก์€ ์Šค๋ ˆ๋“œ์™€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ CPU๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ, ๊ธฐ๋‹ค๋ฆฌ๋Š” ์Šค๋ ˆ๋“œ์˜ spin์ด ๋ฝ์„ ์žก์€ ์Šค๋ ˆ๋“œ์˜ ์ง„ํ–‰์„ ๋ฐฉํ•ดํ•ฉ๋‹ˆ๋‹ค.

๋ฉ€ํ‹ฐ์ฝ”์–ด์—์„œ ์งง์€ critical section์ด๋ฉด spin์ด mutex๋ณด๋‹ค ๋น ๋ฆ…๋‹ˆ๋‹ค โ€” ์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜ ๋น„์šฉ(1~3 ฮผs)๋ณด๋‹ค spin ๋น„์šฉ(์ˆ˜์‹ญ ns)์ด ์ž‘๊ธฐ ๋•Œ๋ฌธ.

์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต Windows CRITICAL_SECTION์˜ spin count ๊ธฐ๋Šฅ์„ ์”๋‹ˆ๋‹ค โ€” ์ฒ˜์Œ์—๋Š” spin์„ ์‹œ๋„ํ•˜๊ณ , ์ผ์ • ํšŸ์ˆ˜ ์•ˆ์— ๋ชป ์žก์œผ๋ฉด ์ปค๋„ ์ง„์ž…(mutex ๋™์ž‘). ๋‘ ๋ฐฉ์‹์˜ ์žฅ์ ์„ ๊ฒฐํ•ฉ. InitializeCriticalSectionAndSpinCount(&cs, 4000)์ด ๊ทธ API.


16. ํ•ต์‹ฌ ์š”์•ฝ ์นด๋“œ (์žฌ๊ฒŒ์žฌ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Race Condition = ๋‘˜ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ์ž์›์— ๋™์‹œ ์ ‘๊ทผ,
                  ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋น„๊ฒฐ์ •์  ํ˜„์ƒ.

๋ฐœ์ƒ 4์กฐ๊ฑด:
  โ‘  ๊ณต์œ  ์ž์› ์กด์žฌ
  โ‘ก ๋‘˜ ์ด์ƒ์ด ๋™์‹œ ์ ‘๊ทผ
  โ‘ข ์ ์–ด๋„ ํ•˜๋‚˜๋Š” ์“ฐ๊ธฐ (write)
  โ‘ฃ ์ˆœ์„œ๋ฅผ OS๊ฐ€ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
                 โ†‘
  ๋„ค ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋งŒ ๊นจ๋„ race ์—†์Œ
  โ†’ ๋น„์šฉ ์ˆœ: โ‘  > โ‘ก > โ‘ข > โ‘ฃ
  โ†’ ์–ธ๋ฆฌ์–ผ์€ โ‘ก๋ฅผ ๊นธ (GameThread ๋‹จ์ผํ™”)
  โ†’ const ๋ฐ์ดํ„ฐ๋Š” โ‘ข์„ ๊นธ
  โ†’ mutex๋Š” โ‘ฃ๋ฅผ ๊นธ

Critical Section = ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ ๊ตฌ๊ฐ„
  ์†์„ฑ: mutual exclusion / progress / bounded waiting / no starvation
  ํฌ๊ธฐ ์ตœ์†Œํ™” โ€” ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์€ ๋ฝ ๋ฐ–์—์„œ

๋™๊ธฐํ™” ๊ฐ์ฒด ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ (๋‚ฎ์Œ โ†’ ๋†’์Œ):
  std::atomic load/store        1~์ˆ˜ ns
  std::atomic CAS               5~20 ns
  CRITICAL_SECTION (no ๊ฒฝํ•ฉ)    ์ˆ˜์‹ญ ns
  std::mutex (no ๊ฒฝํ•ฉ)          ์ˆ˜์‹ญ ns
  SRWLOCK ์ฝ๊ธฐ (no ๊ฒฝํ•ฉ)        ์ˆ˜์‹ญ ns
  CRITICAL_SECTION (๊ฒฝํ•ฉ)       1~3 ฮผs (์ปจํ…์ŠคํŠธ ์Šค์œ„์น˜)
  std::mutex (๊ฒฝํ•ฉ)             1~3 ฮผs
  Mutex ์ปค๋„ ๊ฐ์ฒด               1~3 ฮผs (ํ•ญ์ƒ syscall)
  Semaphore / Event / CV        1~3 ฮผs

Lock-free:
  CAS = Compare-And-Swap (LOCK CMPXCHG)
  weak vs strong (spurious failure ํ—ˆ์šฉ)
  ABA ๋ฌธ์ œ โ€” Aโ†’Bโ†’A ๋ชป ์•Œ์•„์ฑ”
    ํšŒํ”ผ: tagged pointer / hazard pointer / epoch reclamation / GC
  ์ง„ํ–‰ ๋ณด์žฅ: blocking < obstruction-free < lock-free < wait-free

Memory Model (๋ช…๋ น ์žฌ๋ฐฐ์น˜ + ์บ์‹œ ์ผ๊ด€์„ฑ):
  memory_order_relaxed   โ€” ์ˆœ์„œ X
  memory_order_acquire   โ€” load์—, ์ดํ›„ ์•ˆ ์•ž๋‹น๊น€
  memory_order_release   โ€” store์—, ์ด์ „ ์•ˆ ๋’ค๋กœ ๋ฏธ๋ฃธ
  memory_order_acq_rel   โ€” RMW
  memory_order_seq_cst   โ€” ๊ฐ€์žฅ ๊ฐ•ํ•จ (๊ธฐ๋ณธ)
  acquire-release ํŽ˜์–ด โ†’ happens-before ์„ฑ๋ฆฝ
  x86 TSO (๊ฐ•ํ•จ) vs ARM weak (barrier ํ•„์ˆ˜)

๋ณ‘๋ฆฌ:
  Deadlock    = ์„œ๋กœ์˜ ๋ฝ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ฉˆ์ถค
                4์กฐ๊ฑด: ์ƒํ˜ธ๋ฐฐ์ œยท์ ์œ ๋Œ€๊ธฐยท๋น„์„ ์ ยท์ˆœํ™˜๋Œ€๊ธฐ
                ํšŒํ”ผ: lock ordering / std::scoped_lock / timeout
  Livelock    = ๋ฝ ์•ˆ ์žก๊ณ  ์–‘๋ณด๋งŒ โ€” random back-off๋กœ ํ•ด๊ฒฐ
  Starvation  = ํŠน์ • ์Šค๋ ˆ๋“œ ์˜์›ํžˆ ๋ชป ์žก์Œ โ€” ๊ณต์ •์„ฑ ์ •์ฑ…
  Priority Inv = ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ๋ฝ์„ ๋†’์€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๊ธฐ๋‹ค๋ฆผ
                ํ•ด๊ฒฐ: Priority Inheritance / PCP
                ์‚ฌ๋ก€: NASA Pathfinder (1997)

Windows API:
  CRITICAL_SECTION      ์‚ฌ์šฉ์ž ๋ชจ๋“œ ์šฐ์„ , ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค
  SRWLOCK               R/W ๋ถ„๋ฆฌ, ๊ฐ€๋ฒผ์›€
  Mutex (CreateMutex)   ์ปค๋„ ๊ฐ์ฒด, ํ”„๋กœ์„ธ์Šค ๊ฐ„
  Semaphore / Event     ์ปค๋„ ๊ฐ์ฒด
  CONDITION_VARIABLE    ์กฐ๊ฑด ๋Œ€๊ธฐ
  InterlockedExchange   atomic ํ•จ์ˆ˜๊ตฐ

POSIX API:
  pthread_mutex_t       (recursive/errorcheck/robust)
  pthread_rwlock_t      R/W
  pthread_cond_t        Condition Variable
  sem_t                 named/unnamed semaphore

์–ธ๋ฆฌ์–ผ ๋™์‹œ์„ฑ:
  ์ฒ ํ•™ โ€” "๊ณต์œ ๋ฅผ ์•ˆ ๋งŒ๋“ ๋‹ค"
  GameThread (UObject) / RenderThread (proxy) / RHIThread (GPU) ๋ถ„๋ฆฌ
  ENQUEUE_RENDER_COMMAND โ€” ๋ช…๋ น ํ๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  TaskGraph โ€” ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„
  FCriticalSection + FScopeLock (๋“œ๋ฌพ)
  FThreadSafeCounter (atomic)
  TQueue<T, EQueueMode::Spsc/Mpsc> (lock-free)

์„ ํƒ ๊ฐ€์ด๋“œ:
  ๋‹จ์ผ ๊ฐ’ + ๋‹จ์ˆœ ์—ฐ์‚ฐ        โ†’ std::atomic
  ๋ณตํ•ฉ ์—ฐ์‚ฐ                  โ†’ std::mutex
  reader-heavy               โ†’ std::shared_mutex
  ๋งค์šฐ ์งง์€ CS + ๋ฉ€ํ‹ฐ์ฝ”์–ด    โ†’ spin lock
  ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ            โ†’ ์ปค๋„ Mutex / Semaphore (IPC ํšŒ๊ท€)
  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์žฌ์„ค๊ณ„ ์—ฌ์ง€       โ†’ lock-free ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (Boost.Lockfree)

๊ธฐ์–ตํ•  ํ•œ ์ค„:
  "Race๋Š” ๊ณต์œ ยท๋™์‹œยท์“ฐ๊ธฐยท์ˆœ์„œ๋ฌด๋ณด์žฅ์˜ ์‚ฐ๋ฌผ. ๋ฝ์„ ์ž˜ ์“ฐ๋Š” ๊ฒƒ๋ณด๋‹ค ๊ณต์œ ๋ฅผ ์•ˆ ๋งŒ๋“œ๋Š” ๊ฒŒ ๋” ์ข‹์€ ๋‹ต."

17. ํšŒ๊ท€ ๋‹ค๋ฆฌ โ€” ๋‹ค๋ฅธ CS ํŒŒ์ผ ์—ฐ๊ฒฐ

ํŒŒ์ผ์—ฐ๊ฒฐ ์ง€์ 
01_runtime๊ณต์œ  ์ž์›์˜ ์œ„์น˜ โ€” ํž™ยท์ „์—ญยท์ •์  ์˜์—ญ์ด ๋ชจ๋‘ race ๋ฐœ์ƒ ํ›„๋ณด. ์Šคํƒ์€ ์Šค๋ ˆ๋“œ๋ณ„ ๋…๋ฆฝ์ด๋ผ race ์—†์Œ(20๋ฒˆ ํšŒ๊ท€)
03_new_vs_mallocํž™ ํ• ๋‹น์ž ์ž์ฒด๊ฐ€ race condition์˜ ํ”ํ•œ ์ถœ์ฒ˜ โ€” ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ new/malloc์€ ๋‚ด๋ถ€ ๋ฝ ํ•„์š”. ๊ทธ๋ž˜์„œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ• ๋‹น์ž(tcmallocยทjemalloc)๊ฐ€ ๋”ฐ๋กœ ์กด์žฌ
09_rtti_raiistd::lock_guardยทstd::unique_lockยทstd::scoped_lock์ด RAII์˜ ๋Œ€ํ‘œ ์ ์šฉ ์‚ฌ๋ก€. ์˜ˆ์™ธ ๋ฐœ์ƒํ•ด๋„ unlock ๋ˆ„๋ฝ ์—†์Œ
10_pointer_deepdive๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ๊ฐ€ lock-free ์ž๋ฃŒ๊ตฌ์กฐ ABA ๋ฌธ์ œ์˜ ํ•ต์‹ฌ โ€” A๋ฅผ freeํ•œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋‹ค์‹œ A ์ฃผ์†Œ๋กœ ์žกํ˜€ CAS๊ฐ€ ์†์Œ
11_smart_pointershared_ptr์˜ reference count๊ฐ€ atomic ์‚ฌ์šฉ ์‚ฌ๋ก€. ์ฆ๊ฐ€๋Š” memory_order_relaxed, ๊ฐ์†Œ๋Š” memory_order_acq_rel ๋˜๋Š” seq_cst๋กœ ๋งˆ์ง€๋ง‰ ์†Œ๋ฉธ ๊ฐ€์‹œ์„ฑ ๋ณด์žฅ
16_stl_containersSTL ์ปจํ…Œ์ด๋„ˆ๋Š” thread-safeํ•˜์ง€ ์•Š์Œ โ€” ํ•œ ์ปจํ…Œ์ด๋„ˆ์— ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ ์“ฐ๋ฉด race. concurrent ์ปจํ…Œ์ด๋„ˆ(tbb::concurrent_queue, Microsoft PPL concurrent_unordered_map ๋“ฑ) ๋ณ„๋„
19_process_vs_thread์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ฃผ์†Œ ๊ณต๊ฐ„์„ ๊ณต์œ ํ•˜๋ฏ€๋กœ race ๋ฐœ์ƒ โ€” 19๋ฒˆ์ด race์˜ ํ† ๋Œ€. ํ”„๋กœ์„ธ์Šค๋Š” ๊ฒฉ๋ฆฌ๋˜์–ด race ์—†์ง€๋งŒ IPC(22)๋กœ ํ†ต์‹ ํ•˜๋ฉด IPC ์•ˆ์—์„œ race ๋ฐœ์ƒ ๊ฐ€๋Šฅ
20_stack_overflow์Šคํƒ์€ ์Šค๋ ˆ๋“œ๋ณ„ ๋…๋ฆฝ์ด๋ผ ์Šคํƒ ๋ณ€์ˆ˜๋Š” race ์—†์Œ. ํ•˜์ง€๋งŒ ์Šคํƒ ๋ณ€์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— ๋„˜๊ธฐ๋ฉด race + use-after-free ์œ„ํ—˜
21_context_switchingrace์˜ ์‹œ๊ฐ„์  ์›์ธ โ€” ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด ๋ช…๋ น ๋‹จ์œ„๋กœ ์ž„์˜ ์‹œ์ ์— ๋ผ์–ด๋“ค๊ธฐ ๋•Œ๋ฌธ์— count++์˜ 3๋‹จ๊ณ„ ์‚ฌ์ด์—์„œ ์Šค์œ„์น˜๊ฐ€ ์ผ์–ด๋‚จ. ๋™๊ธฐํ™” ๊ฐ์ฒด ๋น„์šฉ๋„ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ(21)์˜ ๋น„์šฉ ์ŠคํŽ™ํŠธ๋Ÿผ๊ณผ ์ง๊ฒฐ
22_ipcํ”„๋กœ์„ธ์Šค ๊ฐ„ race โ€” ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ OS๊ฐ€ ๋™๊ธฐํ™” ์•ˆ ํ•ด์ฃผ๋ฏ€๋กœ Named Mutex/Semaphore/Event(์ปค๋„ ๊ฐ์ฒด)์™€ ์ง์ง€์–ด ์‚ฌ์šฉ. 22๋ฒˆ ยง13.5์˜ โ€œ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ + Named ๋™๊ธฐํ™”โ€ ํŒจํ„ด์ด IPC ์ฐจ์›์˜ race ํšŒํ”ผ
์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

ยฉ GoldBoll. ์ผ๋ถ€ ๊ถŒ๋ฆฌ ๋ณด์œ 

Powered by Jekyll with Chirpy theme

์ธ๊ธฐ ํƒœ๊ทธ