TL;DR

  • thread๋Š” ํ•˜๋“œ์›จ์–ดยทOSยทuser ์„ธ ์ธต์œ„๋ผ ์ด๋ฆ„๋งŒ ๊ฐ™์•„ ๋จผ์ € ๊ตฌ๋ถ„์ด ํ•„์š”
  • ๋™์‹œ์„ฑ์€ ๊ฒน์ณ ๋‹ค๋ฃจ๋Š” ๊ตฌ์กฐ, ๋ณ‘๋ ฌ์„ฑ์€ ์—ฌ๋Ÿฌ ์ฝ”์–ด์˜ ์‹ค์ œ ๋™์‹œ ์‹คํ–‰ โ€” I/O-bound๋Š” ๋™์‹œ์„ฑ, CPU-bound๋Š” ๋ณ‘๋ ฌ๋กœ ํ•ด๊ฒฐ
  • ์‹คํ–‰ ๋ชจ๋ธ์€ preemptive(OS thread)ยทcooperative(event loop)ยทM:N(goroutine) ์„ธ ๊ฐˆ๋ž˜

AI-assisted


1. CPU๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ ์‹คํ–‰

CPU ์ฝ”์–ด ํ•˜๋‚˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค. ํ•œ ์ˆœ๊ฐ„์— ์ง„ํ–‰๋˜๋Š” ์‹คํ–‰ ํ๋ฆ„์€ ํ•˜๋‚˜๋ฟ์ด๋‹ค. ํ˜„์‹ค์˜ ์š”๊ตฌ๋Š” ๋‹ค๋ฅด๋‹ค.

  • ์›น ์„œ๋ฒ„๋Š” ์ˆ˜์ฒœ ๋ช…์˜ ์š”์ฒญ์„ ๋™์‹œ์— ๋ฐ›์•„์•ผ ํ•œ๋‹ค
  • ํฌ๋กค๋Ÿฌ๋Š” ์ˆ˜๋ฐฑ ๊ฐœ์˜ URL์„ ๋™์‹œ์— ์ˆ˜์ง‘ํ•˜๋ ค ํ•œ๋‹ค
  • ํ•œ ํ”„๋กœ๊ทธ๋žจ์ด ํŒŒ์ผ์„ ์ฝ๋Š” ๋™์•ˆ ๋„คํŠธ์›Œํฌ ์š”์ฒญ๋„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค

OS๋Š” CPU ์‹œ๊ฐ„์„ ์ž˜๊ฒŒ ์ชผ๊ฐœ ์—ฌ๋Ÿฌ ์ž‘์—…์— ๋ฒˆ๊ฐˆ์•„ ๋‚˜๋ˆ ์คŒ์œผ๋กœ์จ ์ด ์š”๊ตฌ๋ฅผ ๋ฉ”์šด๋‹ค.
์ฝ”์–ด ํ•˜๋‚˜์—์„œ ๋งค ์ˆœ๊ฐ„ ๋„๋Š” ํ๋ฆ„์€ ํ•˜๋‚˜์ง€๋งŒ ์ „ํ™˜์ด ์ถฉ๋ถ„ํžˆ ๋น ๋ฅด๋ฉด ์—ฌ๋Ÿฌ ์ž‘์—…์ด ํ•จ๊ป˜ ์ง„ํ–‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

๋ฌผ๋ฆฌ ์ฝ”์–ด์™€ ๋…ผ๋ฆฌ ์ฝ”์–ด โ€” SMT

์ตœ์‹  CPU๋Š” ๋ฌผ๋ฆฌ ์ฝ”์–ด ํ•˜๋‚˜๋ฅผ ๋…ผ๋ฆฌ ์ฝ”์–ด ๋‘ ๊ฐœ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ธฐ์ˆ ์„ ๊ฐ–๋Š”๋‹ค(์ธํ…”์˜ ํ•˜์ดํผ์Šค๋ ˆ๋”ฉ, ์ผ๋ฐ˜์ ์œผ๋กœ SMT).
โ€œ4์ฝ”์–ด 8์Šค๋ ˆ๋“œโ€ CPU๋Š” ๋ฌผ๋ฆฌ ์ฝ”์–ด๊ฐ€ 4๊ฐœ์ง€๋งŒ OS์—๋Š” ๋…ผ๋ฆฌ ์ฝ”์–ด๊ฐ€ 8๊ฐœ๋กœ ๋ณด์ธ๋‹ค. ๋…ผ๋ฆฌ ์ฝ”์–ด๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๋” ๋งŽ์€ ํ๋ฆ„์„ ๋™์‹œ์— ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, ๊ฐ™์€ ๋ฌผ๋ฆฌ ์ฝ”์–ด์˜ ์‹คํ–‰ ์ž์›์„ ๋‚˜๋ˆ  ์“ฐ๋ฏ€๋กœ ์„ฑ๋Šฅ์ด ๋ฌผ๋ฆฌ ์ฝ”์–ด ์ˆ˜์˜ ๋ฐฐ์ˆ˜๋งŒํผ ๋Š˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์ด โ€œ8์Šค๋ ˆ๋“œโ€์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ค์Œ ์ ˆ์—์„œ ๊ตฌ๋ถ„ํ•  ํ•˜๋“œ์›จ์–ด thread๋‹ค.


2. ์‹คํ–‰ ๋‹จ์œ„ โ€” process, thread, coroutine

"์Šค๋ ˆ๋“œ"๋ผ๋Š” ๋ง์˜ ์„ธ ์ธต์œ„

๊ฐ™์€ ๋‹จ์–ด๊ฐ€ ์™„์ „ํžˆ ๋‹ค๋ฅธ ์ธต์œ„๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ด๋ฆ„๋งŒ ๊ฐ™์•„ ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šฐ๋‹ˆ ๋จผ์ € ์งš๊ณ  ๊ฐ„๋‹ค.

  • ํ•˜๋“œ์›จ์–ด thread: ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ โ€œ4์ฝ”์–ด 8์Šค๋ ˆ๋“œโ€์˜ ๊ทธ ์Šค๋ ˆ๋“œ. ๋…ผ๋ฆฌ ์ฝ”์–ด์™€ ๊ฐ™์€ ๋ง์ด๊ณ , CPU ์ŠคํŽ™์œผ๋กœ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์ •ํ•ด์ง„๋‹ค.
  • OS thread(kernel-level thread): ํ”„๋กœ๊ทธ๋žจ์ด ๋งŒ๋“ค๊ณ  kernel์ด ์ธ์‹ยท์Šค์ผ€์ค„๋งํ•˜๋Š” ์‹คํ–‰ ํ๋ฆ„(threading.Thread, pthread ๋“ฑ). ๊ฐœ์ˆ˜ ์ œํ•œ์ด ์—†๋‹ค.
  • user thread(user-level thread): kernel์€ ๋ชจ๋ฅด๊ณ  user space ๋Ÿฐํƒ€์ž„์ด ๊ตด๋ฆฌ๋Š” ๊ฒฝ๋Ÿ‰ ์‹คํ–‰ ํ๋ฆ„. ์•„๋ž˜ coroutine๊ณผ 10์ ˆ goroutine์ด ์—ฌ๊ธฐ ์†ํ•œ๋‹ค.

์•ž์œผ๋กœ ๊ทธ๋ƒฅ โ€œthreadโ€๋ผ๊ณ  ํ•˜๋ฉด kernel์ด ์Šค์ผ€์ค„๋งํ•˜๋Š” OS thread๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
OS๋Š” ์ด OS thread๋“ค์„ ๋…ผ๋ฆฌ ์ฝ”์–ด(ํ•˜๋“œ์›จ์–ด thread)์— ๋ฒˆ๊ฐˆ์•„ ์˜ฌ๋ฆฌ๊ณ , ๋…ผ๋ฆฌ ์ฝ”์–ด ํ•˜๋‚˜๊ฐ€ ํ•œ ์ˆœ๊ฐ„์— ์‹คํ–‰ํ•˜๋Š” OS thread๋Š” ํ•˜๋‚˜๋ฟ์ด๋‹ค.
user thread๋Š” ์ด OS thread์— ์–นํ˜€์„œ ๋ˆ๋‹ค.

โ€œ์—ฌ๋Ÿฌ ์ผโ€์„ ๋‹ด๋Š” ์‹คํ–‰ ๋‹จ์œ„๋Š” ์„ธ ์ธต์œ„๋‹ค.

  • process: ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ๊ทธ๋žจ์˜ ์ธ์Šคํ„ด์Šค
    • ๊ฐ process๋Š” ์ž๊ธฐ๋งŒ์˜ ๋…๋ฆฝ๋œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์ด ์žˆ์Œ
    • ๋‹ค๋ฅธ process์˜ ๋ฉ”๋ชจ๋ฆฌ์—๋Š” ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์œผ๋ ค๋ฉด IPC(ํŒŒ์ดํ”„, ์†Œ์ผ“, ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ)๊ฐ€ ํ•„์š”
  • thread: process ์•ˆ์˜ ๋” ์ž‘์€ ์‹คํ–‰ ํ๋ฆ„
    • ๊ฐ™์€ process์˜ thread๋“ค์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ๊ณต์œ (ํž™, ์ „์—ญ ๋ณ€์ˆ˜). ๋‹ค๋งŒ ๊ฐ thread๋Š” ์ž๊ธฐ๋งŒ์˜ ์Šคํƒ๊ณผ ๋ ˆ์ง€์Šคํ„ฐ, program counter๋ฅผ ๋”ฐ๋กœ ๊ฐ€์ง
    • OS(์ •ํ™•ํžˆ๋Š” kernel)๊ฐ€ ์ธ์‹ํ•˜๊ณ  ์Šค์ผ€์ค„๋งํ•˜๋Š” ์ตœ์†Œ ๋‹จ์œ„
  • coroutine: thread๋ณด๋‹ค ํ•œ ๊ฒน ์•ˆ์ชฝ์˜ ์‹คํ–‰ ๋‹จ์œ„
    • ์ค‘๊ฐ„์— ๋ฉˆ์ท„๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๊ทธ ์ž๋ฆฌ์—์„œ ์žฌ๊ฐœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋กœ, kernel์€ ์ด ์กด์žฌ๋ฅผ ๋ชจ๋ฅด๊ณ  ํ•˜๋‚˜์˜ thread ์•ˆ์—์„œ ํ”„๋กœ๊ทธ๋žจ(๋Ÿฐํƒ€์ž„)์ด ์Šค์Šค๋กœ ๊ด€๋ฆฌ
    • ์œ„์—์„œ ๋งํ•œ user thread์˜ ๋Œ€ํ‘œ์ ์ธ ํ˜•ํƒœ์ด๋ฉฐ, ์ž์„ธํ•œ ๋™์ž‘์€ 9์ ˆ event loop์—์„œ ๋‹ค๋ฃธ
processthread
๋ฉ”๋ชจ๋ฆฌ๋…๋ฆฝ(๊ฒฉ๋ฆฌ)๊ฐ™์€ process ์•ˆ์—์„œ ๊ณต์œ 
ํ†ต์‹ IPC ํ•„์š”๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋กœ ๋ฐ”๋กœ
์ƒ์„ฑ ๋น„์šฉ๋ฌด๊ฑฐ์›€์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ€๋ฒผ์›€
ํ•œ์ชฝ์ด ์ฃฝ์œผ๋ฉด๋‹ค๋ฅธ process ์˜ํ–ฅ ์ ์Œprocess ์ „์ฒด ์œ„ํ—˜

๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๊ฐ€ ๋’ค์—์„œ ๋‹ค๋ฃฐ race condition๊ณผ ๋ณ‘๋ ฌํ™” ์ „๋žต์„ ๊ฐ€๋ฅธ๋‹ค.

์…‹์„ โ€œ๋ฌด์—‡์„ ์ž๊ธฐ ๊ฒƒ์œผ๋กœ ์†Œ์œ ํ•˜๋А๋ƒโ€๋กœ ๋ณด๋ฉด, ๋ชจ๋‘ ์‹คํ–‰ ์ƒํƒœ๋ฅผ ๋‹ด์€ ๊ตฌ์ฒด์  ๊ฐ์ฒด์ด๋˜ ์†Œ์œ  ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋ž˜๋กœ ๊ฐˆ์ˆ˜๋ก ์ข์•„์ง„๋‹ค.

์‹คํ–‰ ๋‹จ์œ„์ž๊ธฐ ๊ฒƒ์œผ๋กœ ์†Œ์œ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ๊ด€๋ฆฌ ์ฃผ์ฒด
process๋…๋ฆฝ ์ฃผ์†Œ ๊ณต๊ฐ„, ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ์—†์Œ(๊ฒฉ๋ฆฌ)kernel
thread์ž๊ธฐ ์Šคํƒ + ๋ ˆ์ง€์Šคํ„ฐยทPC์†Œ์† process์˜ ์ฃผ์†Œ ๊ณต๊ฐ„kernel
coroutine์ž๊ธฐ frame(์ง€์—ญ ๋ณ€์ˆ˜ + ์žฌ๊ฐœ ์ง€์ )์†Œ์† thread์˜ ์Šคํƒยทํž™ ์ „๋ถ€user space ๋Ÿฐํƒ€์ž„

process๋Š” ๊ฒฉ๋ฆฌ๋œ ๊ณต๊ฐ„์„ ํ†ต์งธ๋กœ, thread๋Š” ๊ทธ ์•ˆ์—์„œ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ(์Šคํƒยท๋ ˆ์ง€์Šคํ„ฐ)๋งŒ, coroutine์€ ๋” ์•ˆ์ชฝ์—์„œ ์ค‘๋‹จ๋œ frame๋งŒ ์ž๊ธฐ ๊ฒƒ์œผ๋กœ ๋“ ๋‹ค.
coroutine๋„ ์ถ”์ƒ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ผ โ€œ์ค‘๋‹จ ์ƒํƒœ๋ฅผ ๋‹ด์€ ์‹ค์ œ ๊ฐ์ฒดโ€(Python์—์„  async def ํ˜ธ์ถœ์ด ๋Œ๋ ค์คŒ)์ด๋‹ค.


3. ์ดํ•ด๋ฅผ ์œ„ํ•œ ๋น„์œ 

์ด ๊ธ€์€ ์ดํ›„์˜ ๋ชจ๋“  ์„ค๋ช…์„ ํ•˜๋‚˜์˜ ๋น„์œ  ์œ„์—์„œ ํ•œ๋‹ค. ์‚ฌ๋ฌด์‹ค์—์„œ ์ผ๊พผ๋“ค์ด ์„œ๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ทธ๋ฆผ์ด๋‹ค.

๋น„์œ ์‹ค์ฒดํ•ต์‹ฌ ์„ฑ์งˆ
์‚ฌ๋ฌด์‹คprocess์ž๊ธฐ ์ฑ…์ƒ(์ฃผ์†Œ ๊ณต๊ฐ„)๊ณผ ์ž์›์„ ๊ฐ€์ง. ์‚ฌ๋ฌด์‹ค๋ผ๋ฆฌ๋Š” ๊ฒฉ๋ฆฌ
์—…๋ฌด ํด๋”OS thread๋ถ๋งˆํฌ(program counter)์™€ ์ค‘๊ฐ„ ๋ฉ”๋ชจ(์Šคํƒยท๋ ˆ์ง€์Šคํ„ฐ)๋ฅผ ํ’ˆ์Œ
ํด๋” ์•ˆ ์•ˆ๊ฑดcoroutine (user thread)์•ˆ๊ฑด๋งˆ๋‹ค ์ž์ฒด ๋ถ๋งˆํฌ. ํด๋” ์•ˆ์—์„œ๋งŒ ์˜ค๊ฐ
์ผ๊พผ์˜ ์†๋…ผ๋ฆฌ ์ฝ”์–ด(ํ•˜๋“œ์›จ์–ด thread)ํ•œ ์ˆœ๊ฐ„์— ํด๋” ํ•˜๋‚˜๋งŒ ์žก์Œ
๊ด€๋ฆฌ์žOS scheduler์–ด๋А ์†์— ์–ด๋А ํด๋”๋ฅผ ์ฅ์—ฌ์ค„์ง€ ๋ฐฐ์ •
์ฒ˜๋ฆฌ ๋Œ€๊ธฐ ๋ฐ”๊ตฌ๋‹ˆrun queue์ค€๋น„๋œ ํด๋”๊ฐ€ ์†์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค„. ์†(์ฝ”์–ด)๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ
๋ณด๋ฅ˜ํ•จwait queue์™ธ๋ถ€ ๋‹ต๋ณ€์„ ๊ธฐ๋‹ค๋ฆฌ๋А๋ผ ์•„์ง ์ฒ˜๋ฆฌ ๋ชป ํ•˜๋Š” ํด๋”๊ฐ€ ๊ฐ€๋Š” ๊ณณ

ํด๋”(thread)๋Š” ์ˆ˜๋ฐฑ ์žฅ, ์ผ๊พผ์˜ ์†(๋…ผ๋ฆฌ ์ฝ”์–ด)์€ ๋ช‡ ๊ฐœ๋ฟ์ด๋‹ค.
ํ•œ ์†์ด ํด๋” A๋ฅผ ์ž ๊น ์ฒ˜๋ฆฌํ•˜๋‹ค ๋ถ๋งˆํฌ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋‚ด๋ ค๋†“๊ณ  ํด๋” B๋ฅผ ์ง‘๋Š”๋‹ค. ๋‚˜์ค‘์— A๋ฅผ ๋‹ค์‹œ ์ง‘์œผ๋ฉด ๋ถ๋งˆํฌ๋ถ€ํ„ฐ ์ด์–ด๊ฐ„๋‹ค.
์ด โ€œ๋‚ด๋ ค๋†“๊ณ  ๋ถ๋งˆํฌ ๋‚จ๊ธฐ๊ธฐโ€๊ฐ€ 5์ ˆ์˜ context switch๋‹ค.

ํด๋” ์•ˆ์—๋Š” ์—ฌ๋Ÿฌ ์•ˆ๊ฑด(coroutine)์ด ๋“ค์–ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.
์ผ๊พผ์ด ํด๋”๋ฅผ ์žก์œผ๋ฉด ๊ทธ ์•ˆ ์•ˆ๊ฑด๋“ค์„ ๋ฒˆ๊ฐˆ์•„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ํด๋”๋ฅผ ๋ฐ”๊พธ๋Š” ์ผ์€ ๊ด€๋ฆฌ์ž(kernel)๊ฐ€ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํด๋” ์•ˆ ์•ˆ๊ฑด์„ ๋ฐ”๊พธ๋Š” ์ผ์€ ํ”„๋กœ๊ทธ๋žจ์ด ํด๋” ๋‚ด๋ถ€์—์„œ ์•Œ์•„์„œ ํ•œ๋‹ค. kernel์€ ํด๋” ๋‹จ์œ„๊นŒ์ง€๋งŒ ์•ˆ๋‹ค.


4. thread์˜ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ

์ผํ•˜์ง€ ์•Š๋Š” ํด๋”์—๋„ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค. thread๋Š” ๋Š˜ ์…‹ ์ค‘ ํ•˜๋‚˜์˜ ์ƒํƒœ์— ์žˆ๋‹ค.

  • Running: ์ง€๊ธˆ ์–ด๋–ค ์ฝ”์–ด์—์„œ ์‹ค์ œ๋กœ ๋Œ๊ณ  ์žˆ์Œ (์ผ๊พผ ์†์— ๋“ค๋ฆฐ ํด๋”)
  • Runnable: ๋Œ ์ค€๋น„๋Š” ๋๋Š”๋ฐ ์ฝ”์–ด๋ฅผ ๋ชป ์–ป์–ด run queue์—์„œ ์ฐจ๋ก€๋ฅผ ๊ธฐ๋‹ค๋ฆผ (์ฒ˜๋ฆฌ ๋Œ€๊ธฐ ๋ฐ”๊ตฌ๋‹ˆ ์† ํด๋”). ์ฝ”์–ด๋งŒ ๋‚˜๋ฉด ์ฆ‰์‹œ ์‹คํ–‰
  • Blocked: I/O๋‚˜ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋‹ค๋ ค ์ง€๊ธˆ์€ ๋Œ ์ˆ˜ ์—†์Œ (๋ณด๋ฅ˜ํ•จ ์† ํด๋”). run queue์—๋„ ์—†์Œ

run queue๋Š” ๋ณดํ†ต ๋…ผ๋ฆฌ ์ฝ”์–ด๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ์žˆ๋‹ค(per-core run queue). ๊ด€๋ฆฌ์ž(scheduler)๋Š” ์–ด๋А ์ฝ”์–ด์˜ ๋ฐ”๊ตฌ๋‹ˆ์— ํด๋”๋ฅผ ๋„ฃ์„์ง€ ๋ฐฐ์ •ํ•˜๊ณ , ๊ฐ ์ฝ”์–ด๋Š” ์ž๊ธฐ ๋ฐ”๊ตฌ๋‹ˆ์—์„œ ๋‹ค์Œ ํด๋”๋ฅผ ๊บผ๋‚ด ์ฒ˜๋ฆฌํ•œ๋‹ค.

๋ฐ”๊ตฌ๋‹ˆ๊ฐ€ ์ฑ„์›Œ์ง€๋Š” ๊ฒฝ๋กœ๋Š” ์…‹์ด๋‹ค. ์ƒˆ๋กœ ์ƒ์„ฑ๋œ thread, ์„ ์ ๋‹นํ•ด ๋‚ด๋ ค์˜จ thread, ๋ณด๋ฅ˜ํ•จ์—์„œ ๋‹ต๋ณ€์ด ์™€ ๊นจ์–ด๋‚œ thread๋‹ค.

์ƒํƒœ ์ „์ด๋ฅผ ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์ด๋ ‡๋‹ค.

stateDiagram-v2
    [*] --> Runnable: ์ƒ์„ฑ
    Runnable --> Running: ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋””์ŠคํŒจ์น˜
    Running --> Runnable: preemption ยท ํƒ€์ž„์•„์›ƒ
    Running --> Blocked: blocking I/O ยท ์ด๋ฒคํŠธ ๋Œ€๊ธฐ
    Blocked --> Runnable: I/O ์™„๋ฃŒ ยท ์ด๋ฒคํŠธ ๋„์ฐฉ
    Running --> [*]: ์ข…๋ฃŒ

์ „ํ™˜์—๋Š” ๋‘ ๊ณ„๊ธฐ๊ฐ€ ์žˆ๊ณ , ์ด ๋‘˜์ด ์™„์ „ํžˆ ๋‹ค๋ฅด๋‹ค.

  • preemption(์„ ์ ): Running์—์„œ ์‹œ๊ฐ„ ํ• ๋‹น๋Ÿ‰(time slice)์ด ๋๋‚˜ kernel์ด ๊ฐ•์ œ๋กœ ๋บ์Œ
    • run queue๋กœ ๋ฐ”๋กœ ๋Œ์•„๊ฐ(Runnable)
    • ํ•  ์ผ์€ ๋‚จ์•˜๊ณ  ์ฝ”์–ด๋งŒ ๋‚˜๋ฉด ๋‹ค์‹œ ์‹คํ–‰
  • blocking(๋Œ€๊ธฐ): thread๊ฐ€ blocking I/O๋‚˜ ๋ฝ์„ ์ง์ ‘ ํ˜ธ์ถœํ•ด ์Šค์Šค๋กœ ์ง„ํ–‰ ๋ชป ํ•˜๊ฒŒ ๋จ
    • ๋ณด๋ฅ˜ํ•จ์œผ๋กœ ์ด๋™(Blocked).
    • ๊ธฐ๋‹ค๋ฆฌ๋˜ ์ด๋ฒคํŠธ๊ฐ€ ์™€์•ผ ๋น„๋กœ์†Œ Runnable๋กœ ๋ณต๊ท€

Blocked๋Š” kernel์ด โ€œ์–˜๋Š” I/O๋ฅผ ์ž์ฃผ ํ•˜๋‹ˆ ๋ฏธ๋ฆฌ ์žฌ์šฐ์žโ€๊ณ  ์˜ˆ์ธกํ•ด์„œ ๋งŒ๋“œ๋Š” ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋‹ค. thread๊ฐ€ blocking ์—ฐ์‚ฐ์„ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜๋Š” ์ˆœ๊ฐ„์—๋งŒ ์ผ์–ด๋‚œ๋‹ค.
thread์˜ ๊ณผ๊ฑฐ ์ด๋ ฅ์€ ์šฐ์„ ์ˆœ์œ„ ์กฐ์ •์—๋งŒ ์“ฐ์ผ ๋ฟ, ์ƒํƒœ๋ฅผ ๊ฐ•์ œ๋กœ Blocked๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.


5. context switch โ€” ์ „ํ™˜์˜ ๋น„์šฉ๊ณผ ๋‹จ์œ„

๊ด€๋ฆฌ์ž๊ฐ€ ํด๋”๋ฅผ ๋ฐ”๊พธ๋Š” ์ผ์—๋Š” ๋น„์šฉ์ด ๋“ ๋‹ค.
OS scheduler๋Š” CPU ์‹œ๊ฐ„์„ ์ž˜๊ฒŒ ์ชผ๊ฐœ ์—ฌ๋Ÿฌ thread์— ๋ฒˆ๊ฐˆ์•„ ํ• ๋‹นํ•˜๋Š”๋ฐ(preemptive multitasking), ์ „ํ™˜ํ•  ๋•Œ kernel์€ ๋‘ ๊ฐ€์ง€๋ฅผ ํ•œ๋‹ค.

  1. ์ง€๊ธˆ ๋Œ๋˜ thread์˜ ์ƒํƒœ(๋ ˆ์ง€์Šคํ„ฐ, ์Šคํƒ ํฌ์ธํ„ฐ, program counter)๋ฅผ ์ €์žฅํ•œ๋‹ค
  2. ๋‹ค์Œ thread์˜ ์ƒํƒœ๋ฅผ ๋ณต์›ํ•ด ์ด์–ด์„œ ์‹คํ–‰ํ•œ๋‹ค

์ด ์ €์žฅยท๋ณต์› ๊ณผ์ •์ด context switch๋‹ค. ๋‘ ํŠน์ง•์ด ์ค‘์š”ํ•˜๋‹ค.

  • kernel์ด ๊ฐ•์ œ๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋“œ๋Š” ์ž์‹ ์ด ์–ธ์ œ ๋ฉˆ์ถœ์ง€ ๋ชจ๋ฅธ๋‹ค. ํƒ€์ด๋จธ ์ธํ„ฐ๋ŸฝํŠธ๋กœ OS๊ฐ€ ๋ผ์–ด๋“ค์–ด ํ๋ฆ„์„ ๋ฐ”๊พผ๋‹ค.
  • ๋น„์šฉ์ด ๋“ ๋‹ค. ์ƒํƒœ ์ €์žฅยท๋ณต์›์˜ ์ง์ ‘ ๋น„์šฉ์— ๋”ํ•ด, CPU ์บ์‹œ๊ฐ€ ๋ฌดํšจํ™”๋˜๋Š” ๊ฐ„์ ‘ ๋น„์šฉ์ด ๋”ฐ๋ฅธ๋‹ค. thread๋ฅผ ์ˆ˜์ฒœ ๊ฐœ์”ฉ ๋„์šฐ๋ฉด ์ผํ•˜๋Š” ์‹œ๊ฐ„๋ณด๋‹ค ์ „ํ™˜ํ•˜๋Š” ์‹œ๊ฐ„์ด ๋Š˜์–ด ์˜คํžˆ๋ ค ๋А๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค.

์ „ํ™˜์˜ ๋‹จ์œ„๋Š” thread, ๋น„์šฉ์€ process ๊ฒฝ๊ณ„์—์„œ ์ปค์ง„๋‹ค

์ „ํ™˜์˜ ์‹ค์ œ ๋Œ€์ƒ์€ process๊ฐ€ ์•„๋‹ˆ๋ผ thread๋‹ค.
kernel ๊ด€์ ์—์„œ thread์™€ process๋Š” ์‚ฌ์‹ค์ƒ ๊ฐ™์€ ์Šค์ผ€์ค„ ๋‹จ์œ„๋กœ ํ‘œํ˜„๋˜๊ณ (๋ฆฌ๋ˆ…์Šค์—์„  ๋‘˜ ๋‹ค task_struct), scheduler๋Š” ์ด ๋‹จ์œ„๋ฅผ ์ฝ”์–ด์— ์˜ฌ๋ฆฌ๊ณ  ๋‚ด๋ฆฐ๋‹ค. process๋Š” ์ฃผ์†Œ ๊ณต๊ฐ„ยทํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ ๊ฐ™์€ ์ž์›์„ ๋‹ด๋Š” ์ปจํ…Œ์ด๋„ˆ์ผ ๋ฟ์ด๋ผ, ๊ด€๊ฑด์€ ์ง€๊ธˆ ์ „ํ™˜ํ•˜๋Š” thread๊ฐ€ ์–ด๋А process์— ์†ํ•˜๋А๋ƒ๋‹ค.

  • ๊ฐ™์€ process์˜ thread๋ผ๋ฆฌ: ์ฃผ์†Œ ๊ณต๊ฐ„์„ ๊ณต์œ ํ•˜๋ฏ€๋กœ ๋ ˆ์ง€์Šคํ„ฐยท์Šคํƒ ํฌ์ธํ„ฐยทPC๋งŒ ๊ฐˆ์•„ ๋ผ์šฐ๋ฉด ๋œ๋‹ค. ๊ฐ€๋ณ๋‹ค.
  • ๋‹ค๋ฅธ process์˜ thread๋กœ: ์—ฌ๊ธฐ์— ์ฃผ์†Œ ๊ณต๊ฐ„ ์ „ํ™˜(page table ๊ต์ฒด, TLB flush)์ด ์–นํ˜€ ๋” ๋น„์‹ธ๋‹ค. โ€œprocess ๊ฐ„ ์ „ํ™˜์ด ๋ฌด๊ฒ๋‹คโ€๋Š” ๋ง์€ process ์ž์ฒด๋ฅผ ๋ฐ”๊ฟ”์„œ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ process์˜ thread๋กœ ๋„˜์–ด๊ฐ€๋ฉฐ ์ฃผ์†Œ ๊ณต๊ฐ„๊นŒ์ง€ ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ „ํ™˜ ๋น„์šฉ์„ ์ค„์ด๋ ค๋Š” ๋Œ€ํ‘œ ์žฅ์น˜๊ฐ€ cache affinity๋‹ค.
scheduler๋Š” thread๋ฅผ ์ด์ „์— ๋Œ๋˜ ์ฝ”์–ด์— ๋‹ค์‹œ ์˜ฌ๋ฆฌ๋ ค ํ•œ๋‹ค. ๊ทธ ์ฝ”์–ด์˜ ์บ์‹œ์— ์ด thread๊ฐ€ ์“ฐ๋˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์•„์ง ๋‚จ์•„(warm cache) ์žˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๋‹ค๋งŒ ์บ์‹œ๋Š” ์šฉ๋Ÿ‰์ด ์ฐจ๋ฉด ์˜ค๋ž˜๋œ ๊ฒƒ๋ถ€ํ„ฐ ๋ฐ€๋ ค๋‚˜๋ฏ€๋กœ(๋Œ€๋žต LRU), ๋„ˆ๋ฌด ์˜ค๋ž˜ ์ง€๋‚ฌ๊ฑฐ๋‚˜ ๊ทธ ์‚ฌ์ด ๋‹ค๋ฅธ thread๊ฐ€ ๊ทธ ์ฝ”์–ด๋ฅผ ํœ˜์ €์—ˆ์œผ๋ฉด ์ด๋ฏธ ์‹์–ด(cold cache) ์ด๋“์ด ์‚ฌ๋ผ์ง„๋‹ค.
๊ทธ๋ž˜์„œ cache affinity๋Š” ํ™•์ • ๋ณด์žฅ์ด ์•„๋‹ˆ๋ผ ํ™•๋ฅ ์  ํŽธํ–ฅ์ด๋ฉฐ, ๋ถ€ํ•˜๊ฐ€ ํ•œ์ชฝ์— ๋ชฐ๋ฆฌ๋ฉด scheduler๋Š” ์บ์‹œ ์ด๋“์„ ํฌ๊ธฐํ•˜๊ณ  thread๋ฅผ ๋‹ค๋ฅธ ์ฝ”์–ด๋กœ ์˜ฎ๊ธด๋‹ค.

์ด๊ฒƒ์ด ํ”ํžˆ ๋งํ•˜๋Š” โ€œOS ๋ ˆ๋ฒจ context switchโ€๋‹ค. kernel์ด ์ฃผ๋„ํ•˜๊ณ , ๋ฌด๊ฒ๊ณ , ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋“œ๋Š” ํ†ต์ œํ•  ์ˆ˜ ์—†๋‹ค.
์ดํ›„์— ์†Œ๊ฐœํ•  event loop๋‚˜ goroutine์˜ ์ „ํ™˜๊ณผ๋Š” ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋‹ค.


6. ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ

๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ์€ ๋น„์Šทํ•ด ๋ณด์ด์ง€๋งŒ ๋‹ค๋ฅธ ๊ฐœ๋…์ด๋‹ค. ์ด ๋‘˜์„ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋ฉด ์ดํ›„์˜ ๋„๊ตฌ ์„ ํƒ์ด ์ „๋ถ€ ํ๋ ค์ง„๋‹ค.

  • concurrency(๋™์‹œ์„ฑ)๋Š” ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๊ฒน์ณ์„œ ๋‹ค๋ฃจ๋Š” ๋Šฅ๋ ฅ์ด๋‹ค.
    • ํ•œ ์ˆœ๊ฐ„์— ๋…ผ๋ฆฌ ์ฝ”์–ด ํ•˜๋‚˜๊ฐ€ ํ•œ ๊ฐ€์ง€ ์ผ๋งŒ ํ•˜๋”๋ผ๋„, ์ž‘์—… A๋ฅผ ์กฐ๊ธˆ ํ•˜๋‹ค B๋กœ ๋„˜์–ด๊ฐ€๊ณ  ๋‹ค์‹œ A๋กœ ๋Œ์•„์˜ค๋Š” ์‹์œผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด ๋ฐ–์—์„œ ๋ณด๊ธฐ์—” ์—ฌ๋Ÿฌ ์ผ์ด ํ•จ๊ป˜ ์ง„ํ–‰
    • ์ฝ”์–ด๊ฐ€ ํ•˜๋‚˜๋ฟ์ด์–ด๋„ ๋™์‹œ์„ฑ์€ ์„ฑ๋ฆฝ
  • parallelism(๋ณ‘๋ ฌ์„ฑ)์€ ์—ฌ๋Ÿฌ ์ž‘์—…์ด ์‹ค์ œ๋กœ ๊ฐ™์€ ์ˆœ๊ฐ„์— ์„œ๋กœ ๋‹ค๋ฅธ ์ฝ”์–ด์—์„œ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด๋‹ค.
    • ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋™์‹œ์— ๋„๋Š” ๊ฒƒ์ด๋ผ ์ฝ”์–ด๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์—ฌ์•ผ ๊ฐ€๋Šฅ

๋น„์œ ๋กœ ์˜ฎ๊ธฐ๋ฉด ์„ ๋ช…ํ•˜๋‹ค.

  • ๋™์‹œ์„ฑ: ์ผ๊พผ ํ•œ ๋ช…์ด ํด๋” ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋ฒˆ๊ฐˆ์•„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ํด๋” A๋ฅผ ์กฐ๊ธˆ ๋ณด๋‹ค B๋กœ, ๋‹ค์‹œ A๋กœ. ์†์ด ํ•˜๋‚˜๋ผ ๋ฌผ๋ฆฌ์  ๋™์‹œ ์‹คํ–‰์€ ์•„๋‹ˆ์ง€๋งŒ ๋ฐ–์—์„œ๋Š” ์—ฌ๋Ÿฌ ํด๋”๊ฐ€ ํ•จ๊ป˜ ์ง„ํ–‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.
  • ๋ณ‘๋ ฌ์„ฑ: ์ผ๊พผ ์—ฌ๋Ÿฌ ๋ช…์ด ๊ฐ์ž ํด๋”๋ฅผ ํ•˜๋‚˜์”ฉ ์‹ค์ œ๋กœ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•œ๋‹ค.

ํ•œ ๋ฌธ์žฅ ๊ตฌ๋ถ„

๋™์‹œ์„ฑ์€ โ€œ์—ฌ๋Ÿฌ ์ผ์„ ๋‹ค๋ฃจ๋Š” ๊ตฌ์กฐโ€์˜ ๋ฌธ์ œ์ด๊ณ , ๋ณ‘๋ ฌ์„ฑ์€ โ€œ์—ฌ๋Ÿฌ ์ผ์„ ๋™์‹œ์— ํ•˜๋Š” ์‹คํ–‰โ€์˜ ๋ฌธ์ œ๋‹ค. Rob Pike์˜ ํ‘œํ˜„์œผ๋กœ โ€œConcurrency is not parallelism.โ€

๋™์‹œ์„ฑ์€ ๋ณ‘๋ ฌ์„ฑ์˜ ์ „์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ์ž‘์—…์„ ์ž˜๊ฒŒ ์ชผ๊ฐœ ๋‘๋ฉด ์ฝ”์–ด๊ฐ€ ๋Š˜์—ˆ์„ ๋•Œ ๋ณ‘๋ ฌ๋กœ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค.
๋‘˜์€ ๊ฐ™์€ ๋ง์ด ์•„๋‹ˆ๋‹ค. ์ฝ”์–ด ํ•˜๋‚˜๋กœ๋„ ๋™์‹œ์„ฑ์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋ณ‘๋ ฌ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.


7. CPU-bound์™€ I/O-bound

๋„๊ตฌ๋ฅผ ๊ณ ๋ฅด๋ ค๋ฉด ๋จผ์ € ์ž‘์—…์ด ๋А๋ฆฐ ์›์ธ์„ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•œ๋‹ค.

  • CPU-bound: CPU๊ฐ€ ์‰ฌ์ง€ ์•Š๊ณ  ๊ณ„์‚ฐํ•˜๋А๋ผ ๋А๋ฆฐ ์ž‘์—….
    • ํฐ ๋ฐฐ์—ด ์ •๋ ฌ, ์ด๋ฏธ์ง€ยท์˜์ƒ ๋ณ€ํ™˜, ์•”ํ˜ธํ™”ยท์••์ถ•, ๋จธ์‹ ๋Ÿฌ๋‹ ์ „์ฒ˜๋ฆฌ ๋“ฑ
    • ๋นจ๋ผ์ง€๋ ค๋ฉด ๊ณ„์‚ฐ์„ ๋” ๋งŽ์€ ์ฝ”์–ด์— ๋‚˜๋ˆ ์•ผ ํ•จ
  • I/O-bound: CPU๋Š” ๊ฑฐ์˜ ๋†€๊ณ  ์™ธ๋ถ€ ์ž์›์˜ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋А๋ผ ๋А๋ฆฐ ์ž‘์—….
    • HTTP ์‘๋‹ต ๋Œ€๊ธฐ, DB ์ฟผ๋ฆฌ, ํŒŒ์ผ ์ฝ๊ธฐ, ์†Œ์ผ“ ๋ฐ์ดํ„ฐ ๋„์ฐฉ ๋Œ€๊ธฐ ๋“ฑ
    • ๋นจ๋ผ์ง€๋ ค๋ฉด ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์„œ๋กœ ๊ฒน์ณ์•ผ ํ•จ

์ด ๊ตฌ๋ถ„์ด ๋„๊ตฌ ์„ ํƒ์˜ ๊ฐˆ๋ฆผ๊ธธ์ด๋‹ค.

  • ๊ณ„์‚ฐ์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฌธ์ œ(CPU-bound)๋Š” โ€œ์–ด๋–ป๊ฒŒ ์—ฌ๋Ÿฌ ์ฝ”์–ด๋ฅผ ์“ธ๊นŒโ€, ๊ณง ๋ณ‘๋ ฌ์„ฑ์˜ ๋ฌธ์ œ๋‹ค.
  • ๋Œ€๊ธฐ๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฌธ์ œ(I/O-bound)๋Š” โ€œ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ผ์„ ์–ด๋–ป๊ฒŒ ์‹œํ‚ฌ๊นŒโ€, ๊ณง ๋™์‹œ์„ฑ์˜ ๋ฌธ์ œ๋‹ค.

CPU์™€ ๋„คํŠธ์›Œํฌ์˜ ์†๋„ ์ฐจ๋Š” ํฌ๋‹ค. CPU ์—ฐ์‚ฐ์€ ๋„คํŠธ์›Œํฌ ์™•๋ณต๋ณด๋‹ค 10๋งŒ ๋ฐฐ ์ด์ƒ ๋น ๋ฅด๋‹ค.
๊ทธ๋ž˜์„œ ๋„คํŠธ์›Œํฌ I/O๊ฐ€ ๋งŽ์€ ํ”„๋กœ๊ทธ๋žจ์€ ์ „์ฒด ์‹œ๊ฐ„์˜ ๋Œ€๋ถ€๋ถ„์„ ๋Œ€๊ธฐ๋กœ ํ˜๋ ค๋ณด๋‚ด๋ฉฐ, ์ด ๋Œ€๊ธฐ๋ฅผ ๊ฒน์น˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์ฒด๊ฐ ์†๋„๊ฐ€ ํฌ๊ฒŒ ์˜ค๋ฅธ๋‹ค.


8. ์ง๊ตํ•˜๋Š” ๋‘ ์ถ• โ€” synchronous/asynchronous, blocking/non-blocking

7์ ˆ์—์„œ I/O-bound์˜ ํ•ด๋ฒ•์€ โ€œ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์„œ๋กœ ๊ฒน์น˜๋Š” ๊ฒƒโ€์ด๋ผ ํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ ค๋ฉด ํ•˜๋‚˜์˜ ํ˜ธ์ถœ์ด ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์–ด๋–ป๊ฒŒ ํ–‰๋™ํ•˜๋Š”์ง€๋ถ€ํ„ฐ ์ •๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
์—ฌ๊ธฐ์—” ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ์งˆ๋ฌธ์ด ์„ž์—ฌ ์žˆ๋‹ค: ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ํ˜ธ์ถœ์ž์˜ ํ๋ฆ„์ด ๋ฉˆ์ถ”๋‚˜, ์™„๋ฃŒ๋ฅผ ๋‚ด๊ฐ€ ์ฑ™๊ธฐ๋‚˜ ํ†ต์ง€๋ฐ›๋‚˜.

์ž์ฃผ ํ•œ ๋ฉ์–ด๋ฆฌ๋กœ ๋ญ‰๋šฑ๊ทธ๋ ค์ง€์ง€๋งŒ ์‚ฌ์‹ค์€ ๋…๋ฆฝ๋œ ๋‘ ์ถ•์ด๊ณ , ๋’ค์— ๋‚˜์˜ฌ event loopยทawaitยทFuture๊ฐ€ ์ „๋ถ€ ์ด ์กฐํ•ฉ์œผ๋กœ ์„ค๋ช…๋œ๋‹ค.

synchronous / asynchronous โ€” โ€œ์™„๋ฃŒ๋ฅผ ๋‚ด๊ฐ€ ๋ฌป๋‚˜, ๋ฐฐ๋‹ฌ๋˜๋‚˜โ€

ํ˜ธ์ถœ๊ณผ ๊ฒฐ๊ณผ๊ฐ€ ์‹œ๊ฐ„์ ์œผ๋กœ ๋ฌถ์—ฌ ์žˆ๋Š”์ง€๋ฅผ ๊ฐ€๋ฅธ๋‹ค. ๋” ๋˜๋ ทํ•˜๊ฒŒ๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ฑ…์ž„์ด ํ˜ธ์ถœํ•œ ํ๋ฆ„์— ์žˆ๋А๋ƒ, ํ†ต์ง€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋„˜์–ด๊ฐ€๋А๋ƒ์˜ ๋ฌธ์ œ๋‹ค. ์—ฌ๊ธฐ์„œ โ€œํ๋ฆ„โ€์€ OS thread๊ฐ€ ์•„๋‹ˆ๋ผ ํ˜ธ์ถœ ์Šคํƒ ํ˜น์€ ํ˜ธ์ถœ์„ ์‹คํ–‰ํ•œ ์ฝ”๋ฃจํ‹ด์„ ์˜๋ฏธํ•œ๋‹ค.

  • synchronous: ๊ฒฐ๊ณผ๋ฅผ ํ˜ธ์ถœํ•œ ํ๋ฆ„์ด ์ง์ ‘ ์–ป๋Š”๋‹ค (pull).
    • ๊ธฐ๋‹ค๋ ค์„œ ๋ฐ›๋“  ๋˜๋ฌผ์–ด์„œ ๋ฐ›๋“ , โ€œ๋๋‚ฌ๋‚˜?โ€๋ฅผ ํ™•์ธํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ํ˜ธ์ถœํ•œ ํ๋ฆ„ ์ž์‹ ์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๊ธฐ ์ „์—” ๊ทธ ํ๋ฆ„์ด ํ˜ธ์ถœ ์ง€์ ์„ ๋ฒ—์–ด๋‚˜์ง€ ๋ชปํ•จ
    • โ€œ์š”์ฒญ โ†’ ๊ธฐ๋‹ค๋ฆผ/ํ™•์ธ โ†’ ๊ฒฐ๊ณผ ์‚ฌ์šฉโ€์ด ํ•œ ํ๋ฆ„์œผ๋กœ ์ด์–ด์ง
  • asynchronous: ํ˜ธ์ถœํ•œ ํ๋ฆ„์€ ์ž‘์—…๋งŒ ๊ฑธ์–ด๋‘๊ณ  ์ œ์–ด๋ฅผ ๋†“๋Š”๋‹ค (hand off).
    • ์™„๋ฃŒ ํ™•์ธ์€ ํ๋ฆ„์ด ์•„๋‹ˆ๋ผ ๋Ÿฐํƒ€์ž„(event loop ๋“ฑ)์ด ๋งก๊ณ , ๋๋‚˜๋ฉด ์ฝœ๋ฐฑยท์ด๋ฒคํŠธยทawait ์žฌ๊ฐœ๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐฐ๋‹ฌ๋œ๋‹ค(push)
    • ๋‚ด๊ฐ€ ๋‹ค์‹œ ๋ฌป์ง€ ์•Š์•„๋„ ํ†ต์ง€๊ฐ€ ์˜ค๊ณ (await, ์ฝœ๋ฐฑ, Future.result() ๋“ฑ), โ€œ์ž‘์—… ์‹œ์ž‘โ€๊ณผ โ€œ๊ฒฐ๊ณผ ์†Œ๋น„โ€๊ฐ€ ๋ถ„๋ฆฌ๋œ๋‹ค

์‹๋‹น์œผ๋กœ ์น˜๋ฉด synchronous๋Š” ์นด์šดํ„ฐ์— ์„œ์„œ ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜(blocking) 10์ดˆ๋งˆ๋‹ค โ€œ๋์–ด์š”?โ€๋ฅผ ๋˜๋ฌป๋Š” ๊ฒƒ(non-blocking)์ด๊ณ ,
asynchronous๋Š” ์ง„๋™๋ฒจ์„ ๋ฐ›์•„ ์ž๋ฆฌ์—์„œ ๋”ด ์ผ์„ ํ•˜๋‹ค ๋ฒจ์ด ์šธ๋ฆฌ๋ฉด ๋ฐ›์œผ๋Ÿฌ ๊ฐ€๋Š” ๊ฒƒ์ด๋‹ค. ์ด ์ง„๋™๋ฒจ์ด ์ฝœ๋ฐฑยท์ด๋ฒคํŠธ ํ†ต์ง€์ด๋ฉฐ, ๋‚ด๊ฐ€ ์•ˆ ๋ฌผ์–ด๋ด๋„ ์ €์ชฝ์ด ์•Œ๋ ค์ค€๋‹ค๋Š” ๊ฒŒ asynchronous์˜ ๋ณธ์งˆ์ด๋‹ค.

์ด ์ง„๋™๋ฒจ์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“  ๊ฒƒ์ด Future๋‹ค(์–ธ์–ด์— ๋”ฐ๋ผ Promise).
async ํ˜ธ์ถœ์€ ๊ฒฐ๊ณผ ๋Œ€์‹  ๋นˆ ์ƒ์ž(Future)๋ฅผ ์ฆ‰์‹œ ๋Œ๋ ค์ฃผ๊ณ , ์ž‘์—…์ด ๋๋‚˜๋ฉด ๊ทธ ์ƒ์ž๊ฐ€ ์ฑ„์›Œ์ง„๋‹ค. future.result()๋กœ ๋‚ด๊ฐ€ ๊ฐ’์„ ๋ถ€๋ฅด๋ฉด pull(์•„์ง์ด๋ฉด ๊ทธ ์ž๋ฆฌ์„œ blocking์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜ asyncio์˜ ๊ฒฝ์šฐ exception์„ ๋˜์ง), await๋‚˜ ์™„๋ฃŒ ์ฝœ๋ฐฑ์œผ๋กœ ํ†ต์ง€๋ฐ›์œผ๋ฉด push๋‹ค.

Future ์ž์ฒด๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‹ด๊ธธ ์ž๋ฆฌ์ผ ๋ฟ์ด๊ณ , ์‹ค์ œ ์ž‘์—…์€ Executor(์Šค๋ ˆ๋“œํ’€ยทํ”„๋กœ์„ธ์Šคํ’€)๋‚˜ event loop๊ฐ€ ์ˆ˜ํ–‰ํ•ด ์ฑ„์šด๋‹ค.
(awaitยทevent loop์˜ ๋™์ž‘์€ 9์ ˆ, Executor๋Š” 16์ ˆ์—์„œ ์ด์–ด์ง)

๋‹จ, ์ด ๊ตฌ๋ถ„์€ ๊ฒฐ๊ณผ๊ฐ€ ์ด ์Šค๋ ˆ๋“œ ๋ฐ–์—์„œ ๋‚˜์˜ค๋Š” ํ˜ธ์ถœ์—์„œ๋งŒ ์˜๋ฏธ๊ฐ€ ์žˆ์œผ๋ฉฐ ์ด๋Š” I/O(๋„คํŠธ์›ŒํฌยทํŒŒ์ผยทDBยทํƒ€์ด๋จธ)๊ฐ€ ์ด์— ํ•ด๋‹นํ•œ๋‹ค.
์ˆœ์ˆ˜ ๊ณ„์‚ฐ ํ•จ์ˆ˜(compute(x) ๊ฐ™์€ ๋กœ์ปฌ ํ˜ธ์ถœ)๋Š” ์ด ์Šค๋ ˆ๋“œ๊ฐ€ ์ž๊ธฐ CPU๋กœ ์ง์ ‘ ์‹คํ–‰ํ•˜๋‹ˆ โ€œ๋‚˜์ค‘์— ๋ฐ›๋Š”๋‹คโ€๋Š” ์„ ํƒ์ง€๊ฐ€ ์—†์–ด ์–ธ์ œ๋‚˜ synchronous๋‹ค. ๊ณ„์‚ฐ์„ async๋กœ ๋Œ๋ฆฌ๋ ค๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œยทํ”„๋กœ์„ธ์Šค์— ์œ„์ž„ํ•ด์•ผ ํ•˜๊ณ , ๊ทธ ์ˆœ๊ฐ„ ๊ทธ ํ˜ธ์ถœ์€ ์ด๋ฏธ I/O์ฒ˜๋Ÿผ โ€œ๋งก๊ธฐ๊ณ  ๋‚˜์ค‘์— ํšŒ์ˆ˜โ€ํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋œ๋‹ค.

blocking / non-blocking โ€” โ€œ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ํ˜ธ์ถœ์ž์˜ ํ๋ฆ„์ด ๋ฉˆ์ถ”๋‚˜โ€

์ œ์–ด๊ถŒ์˜ ์ง„ํ–‰ ์—ฌ๋ถ€๋ฅผ ๊ฐ€๋ฅธ๋‹ค. ์ด ์ถ•์€ ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ thread์˜ ์ƒํƒœ๋กœ ๊ณง์žฅ ๋Œ€์‘๋œ๋‹ค.

  • blocking: ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ ์‹คํ–‰ ํ๋ฆ„์ด ๋ฉˆ์ถค
    • ์ •ํ™•ํžˆ๋Š” โ€œํ•„์š”ํ•˜๋ฉด ๊ธฐ๋‹ค๋ฆด ์˜ํ–ฅโ€์ด๋ผ, ์‹ค์ œ๋กœ ๊ธฐ๋‹ค๋ ค์•ผ ํ•  ๋•Œ kernel์ด ๊ทธ thread๋ฅผ ์žฌ์›Œ Blocked๋กœ ๋ณด๋ƒ„
    • ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์ค€๋น„๋ผ ์žˆ์œผ๋ฉด blocking ํ˜ธ์ถœ๋„ ์•ˆ ์ž๊ณ  ๋ฐ”๋กœ ๋Œ์•„์˜ด
  • non-blocking: ์ง€๊ธˆ ๋‹น์žฅ ๋๋‚ผ ์ˆ˜ ์—†์œผ๋ฉด ์ฆ‰์‹œ ์ œ์–ด๊ถŒ์„ ๋Œ๋ ค์คŒ
    • thread๋Š” ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  Running/Runnable์„ ์œ ์ง€ํ•˜๋ฉฐ, ๊ฒฐ๊ณผ๋Š” ๋‚˜์ค‘์— ๋‹ค์‹œ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ํ†ต์ง€๋ฐ›๋Š”๋‹ค.

sync/async๊ฐ€ ํ—ท๊ฐˆ๋ฆฌ๋Š” ์ด์œ 

blocking/non-blocking์€ thread์˜ CPU ์ƒํƒœ, ์ฆ‰ Running์ด๋ƒ Blocked๋ƒ๋กœ ๊ทธ๋Œ€๋กœ ๋งคํ•‘๋˜๋Š” machine ๋™์ž‘ ์ž์ฒด๋ผ ์ง๊ด€์ ์ด๋‹ค.

๋ฐ˜๋ฉด synchronous/asynchronous๋Š” ๋Œ€์‘๋˜๋Š” CPU ์ƒํƒœ๊ฐ€ ์—†๋‹ค.
ํ•˜๋“œ์›จ์–ด ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ โ€œ์™„๋ฃŒ๋ฅผ pull ํ•˜๋А๋ƒ push ๋ฐ›๋А๋ƒโ€๋Š” ์ œ์–ด ํ๋ฆ„์˜ ๊ณ„์•ฝ์ด๋ผ ํ•œ ๋‹จ๊ณ„ ๋” ์ถ”์ƒ์ ์ด๋‹ค.

๋˜ ํ•˜๋‚˜์˜ ํ•จ์ •์€ โ€œํ™•์ธํ•˜๋Š” ์ฃผ์ฒดโ€๋ฅผ thread๋กœ ์˜คํ•ดํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋‹จ์ผ thread asyncio์—์„  ์™„๋ฃŒ๋ฅผ ํ™•์ธํ•˜๋Š” event loop๋„ ๊ฒฐ๊ตญ ๊ฐ™์€ thread์—์„œ ๋ˆ๋‹ค. ๋ฌผ๋ฆฌ์  thread๋Š” ๊ฐ™์•„๋„ ๋…ผ๋ฆฌ์ ์œผ๋กœ ํ™•์ธํ•˜๋Š” ํ๋ฆ„์ด ๋‹ค๋ฅด๋‹ค: synchronous๋Š” ํ˜ธ์ถœํ•œ ์ฝ”๋ฃจํ‹ด ์ž์‹ ์ด, asynchronous๋Š” event loop์ด ํ™•์ธํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ์ถ•์€ โ€œ์–ด๋А thread๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋‚˜โ€๊ฐ€ ์•„๋‹ˆ๋ผ โ€œํ˜ธ์ถœํ•œ ํ๋ฆ„์ด ๊ฒฐ๊ณผ ํ™•์ธ์— ๋งค์—ฌ ์žˆ๋‚˜โ€๋กœ ๋ด์•ผ ํ•œ๋‹ค.

๋‘˜์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ธต์œ„์˜ ์งˆ๋ฌธ์ด๋ผ ์ง๊ตํ•˜๊ณ , ๊ทธ๋ž˜์„œ ์กฐํ•ฉ์ด ๋„ท ๋‚˜์˜จ๋‹ค.

๋‘ ์ถ•์„ 2ร—2๋กœ ๋ณด๋ฉด

blockingnon-blocking
synchronous๊ฐ€์žฅ ํ”ํ•จ. requests.get(), ๊ธฐ๋ณธ ์†Œ์ผ“ recv(), time.sleep()์ €์ˆ˜์ค€์—์„œ ๊ฐ€๋Šฅ. non-blocking ์†Œ์ผ“์„ recv()๋กœ ์ฆ‰์‹œ ํ™•์ธ
asynchronous๊ฑฐ์˜ ์—†์Œ(์‚ฌ์‹ค์ƒ ์•ˆํ‹ฐํŒจํ„ด)asyncio์˜ ์ด์ƒํ˜•. await๋กœ ๋“ฑ๋กํ•˜๊ณ  ์ฆ‰์‹œ ์–‘๋ณด, ๊ฒฐ๊ณผ๋Š” ๋‚˜์ค‘์—
  • sync + blocking: ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ๊ธฐ๋‹ค๋ฆฌ๊ณ , ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ thread๊ฐ€ Blocked๋กœ ์ž ๋“ ๋‹ค. ๊ฐ€์žฅ ํ”ํ•œ ํ˜•ํƒœ๋‹ค.
  • sync + non-blocking: ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜๋˜ ์•„์ง์ด๋ฉด ์ฆ‰์‹œ ๋Œ์•„์˜ค๊ณ , ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋˜๋ฌป๋Š”๋‹ค(polling). thread๋Š” Running์„ ์œ ์ง€ํ•œ๋‹ค.
  • async + blocking: ํ†ต์ง€๋ฅผ ๋“ฑ๋กํ•ด ๋†“๊ณ  ๊ทธ ํ†ต์ง€๋ฅผ ๋‹ค์‹œ blocking์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ผด์ด๋ผ ๋‘ ๋ฐฉ์‹์˜ ์ด์ ์ด ์ƒ์‡„๋˜๋Š” ์•ˆํ‹ฐํŒจํ„ด์ด๋‹ค.
  • async + non-blocking: ๋“ฑ๋กํ•˜๊ณ  ์ฆ‰์‹œ ์–‘๋ณดํ•œ ๋’ค ๊ฒฐ๊ณผ๋Š” ๋‚˜์ค‘์— ํ†ต์ง€๋ฐ›๋Š”๋‹ค. asyncio์˜ ์ด์ƒํ˜•์ด๋‹ค.

์—ฌ๊ธฐ์„œ ํ”ํžˆ ์ƒ๊ธฐ๋Š” ๋Œ€ํ‘œ์ ์ธ ์˜คํ•ด๋“ค์„ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž

  • โ€œasynchronous = ๋น ๋ฆ„โ€์ด ์•„๋‹ˆ๋‹ค.
    • asynchronous๋Š” ํ๋ฆ„ ์ œ์–ด ๋ฐฉ์‹์ผ ๋ฟ์ด๋‹ค.
  • โ€œsynchronous = CPU๋ฅผ ์˜ค๋ž˜ ์“ด๋‹คโ€๋„ ์•„๋‹ˆ๋‹ค.
    • synchronous ํ˜ธ์ถœ๋„ ๋Œ€๋ถ€๋ถ„์˜ ์‹œ๊ฐ„์„ ๋Œ€๊ธฐ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
    • blocking I/O ์ค‘์—๋Š” ๊ทธ thread๊ฐ€ Blocked๋กœ ๊ฐ€๊ณ  ์ฝ”์–ด๋Š” ๋‹ค๋ฅธ thread๊ฐ€ ์“ด๋‹ค(4์ ˆ).
  • synchronous + non-blocking์€ ๊ฐ€๋Šฅํ•˜๋‹ค.
    • non-blocking ์†Œ์ผ“์„ while True: recv()๋กœ ๊ณ„์† ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์ธ๋ฐ, ์ด๋Ÿฌ๋ฉด CPU๋ฅผ ํƒœ์šฐ๋Š” busy polling์ด ๋œ๋‹ค.
    • ๊ทธ๋ž˜์„œ ๋ณดํ†ต์€ OS์˜ ๊ฐ์‹œ ๊ธฐ๋Šฅ(selectยทepoll)์œผ๋กœ ์ค€๋น„๋œ ์ž์›๋งŒ ํ™•์ธํ•œ๋‹ค(11์ ˆ).
    • ๋‹ค๋งŒ ์ €์ง€์—ฐ์ด ๊ด€๊ฑด์ธ ์‹œ์Šคํ…œ(๊ณ ๋นˆ๋„ ํŠธ๋ ˆ์ด๋”ฉ, ์ปค๋„ ์šฐํšŒ ํŒจํ‚ท ์ฒ˜๋ฆฌ)์—์„œ๋Š” thread๋ฅผ ์žฌ์› ๋‹ค ๊นจ์šฐ๋Š” ์ง€์—ฐ์กฐ์ฐจ ์•„๊นŒ์›Œ, ์ด busy polling์„ ์ผ๋ถ€๋Ÿฌ ์“ฐ๊ธฐ๋„ ํ•œ๋‹ค.

9. ๋‘ ๊ฐˆ๋ž˜ โ€” preemptive์™€ cooperative

์ด์ œ ์ด ํ† ๋Œ€ ์œ„์—์„œ ๊ตฌํ˜„๋œ ๋™์‹œ์„ฑ์€ ํฌ๊ฒŒ ๋‘ ๊ฐˆ๋ž˜์ด๊ณ , ์ „ํ™˜์ด ๋ˆ„๊ฐ€ยท์–ธ์ œยท์–ผ๋งˆ๋‚˜ ๋น„์‹ธ๊ฒŒ ์ผ์–ด๋‚˜๋Š”์ง€์—์„œ ๊ฐˆ๋ฆฐ๋‹ค.
ํ•˜๋‚˜๋Š” kernel์ด ๊ฐ•์ œ๋กœ ํ๋ฆ„์„ ๋ฐ”๊พธ๋Š” preemptive, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ฝ”๋“œ๊ฐ€ ์Šค์Šค๋กœ ์‹คํ–‰๊ถŒ์„ ๋„˜๊ธฐ๋Š” cooperative๋‹ค.

(A) OS thread ๊ฐ„ ์ „ํ™˜ โ€” preemptive

5์ ˆ์—์„œ ๋ณธ ๋Œ€๋กœ kernel์ด ๊ฐ•์ œ๋กœ context switch ํ•œ๋‹ค.
์ฝ”๋“œ๋Š” ํ‰๋ฒ”ํ•œ synchronous ๋ฐฉ์‹์œผ๋กœ ์งœ๊ณ , OS๊ฐ€ ์•Œ์•„์„œ ๋ฒˆ๊ฐˆ์•„ ๋Œ๋ฆฐ๋‹ค. thread๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๊ณ , process๋Š” ๊ฒฉ๋ฆฌ๋œ๋‹ค.

  • ์ „ํ™˜์˜ ์ฃผ์ฒด: kernel(๊ฐ•์ œ, ์ฝ”๋“œ๋Š” ์•Œ์ง€ ๋ชปํ•จ)
  • ๋น„์šฉ: ํผ(kernel ๊ฒฝ์œ  + ์บ์‹œ ์˜ํ–ฅ)
  • ๋ณ‘๋ ฌ: ๊ฐ€๋Šฅ(์—ฌ๋Ÿฌ ์ฝ”์–ด์— ์‹ค์ œ๋กœ ๋ฐฐ์น˜)
  • ์œ„ํ—˜: ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ thread์˜ race condition, ์ „ํ™˜ ๋น„์šฉ

(B) coroutine ๊ฐ„ ์ „ํ™˜ โ€” cooperative

ํ•˜๋‚˜์˜ OS thread ์•ˆ์—์„œ ์—ฌ๋Ÿฌ coroutine์ด ๋Œ์•„๊ฐˆ ๋•Œ, ๊ทธ ์œ„์—์„œ event loop๊ฐ€ ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉฐ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” coroutine์„ ๊ณจ๋ผ ๋Œ๋ฆฐ๋‹ค.
ํ•œ coroutine์ด await๋กœ ๋Œ€๊ธฐ์— ๋“ค์–ด๊ฐ€๋ฉด event loop๋Š” ๊ทธ๊ฒƒ์„ ์ œ์ณ๋‘๊ณ  ๋‹ค์Œ coroutine์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฉฐ, ๋Œ€๊ธฐ๊ฐ€ ๋๋‚˜๋ฉด ๋ฉˆ์ท„๋˜ ์ง€์ ๋ถ€ํ„ฐ ์žฌ๊ฐœํ•œ๋‹ค.
์ฝ”๋“œ๊ฐ€ await์—์„œ ์Šค์Šค๋กœ ์‹คํ–‰๊ถŒ์„ ๋„˜๊ธฐ๋ฏ€๋กœ ์ด ๋ฐฉ์‹์„ cooperative multitasking์ด๋ผ ํ•œ๋‹ค. kernel์„ ๊ฑฐ์น˜์ง€ ์•Š๋Š” ์œ ์ € ๊ณต๊ฐ„ ์ „ํ™˜์ด๋ผ ๋น„์šฉ์ด ์ž‘๋‹ค.

๋น„์œ ๋กœ ์˜ฎ๊ธฐ๋ฉด, ์ด๊ฑด ์•ˆ๊ฑด์ด ์ž”๋œฉ ๋“  ํด๋” ํ•˜๋‚˜๋‹ค. ์ผ๊พผ์˜ ์†(๋…ผ๋ฆฌ ์ฝ”์–ด)์ด ํ•˜๋‚˜๋ฟ์ธ ์ด ํด๋”๋ฅผ ์žก๊ณ , ๊ทธ ์•ˆ ์•ˆ๊ฑด(coroutine)๋“ค์„ ๋ฒˆ๊ฐˆ์•„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
ํ•œ ์•ˆ๊ฑด์ด โ€œ์™ธ๋ถ€ ๋‹ต๋ณ€ ๋Œ€๊ธฐโ€(await)์— ๊ฑธ๋ฆฌ๋ฉด ์ž์ฒด ๋ถ๋งˆํฌ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๊ฐ™์€ ํด๋”์˜ ๋‹ค์Œ ์•ˆ๊ฑด์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค. ํด๋”๊ฐ€ ํ•˜๋‚˜๋ผ ์†์ด ์—ด ๊ฐœ์—ฌ๋„ ํ•œ ์†๋งŒ ์ด ํด๋”๋ฅผ ์žก์„ ์ˆ˜ ์žˆ์–ด ๋ณ‘๋ ฌ์€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹  ์•ˆ๊ฑด๋“ค์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ์„œ๋กœ ๊ฒน์ณ์ง€๋‹ˆ ์ผ๊พผ์ด ๋…ธ๋Š” ์‹œ๊ฐ„ ์—†์ด ์—ฌ๋Ÿฌ ์•ˆ๊ฑด์„ ๋ฐ€์–ด๋ถ™์ธ๋‹ค.

event loop์€ ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ผ "์Šค์ผ€์ค„๋Ÿฌ ๊ฐ์ฒด"๋‹ค

processยทthreadยทcoroutine์ด ๊ฐ์ž ์‹คํ–‰ ์ƒํƒœ๋ฅผ ๋‹ด์€ ์‹คํ–‰ ๋‹จ์œ„๋ผ๋ฉด, event loop์€ ๊ทธ๊ฒƒ๋“ค์„ ๊ณ ๋ฅด๊ณ  ๊ตด๋ฆฌ๋Š” ๊ตฌ๋™๊ธฐ๋‹ค.
์ถ”์ƒ ๊ฐœ๋…๋งŒ์ด ์•„๋‹ˆ๋ผ ๊ตฌํ˜„์—์„  ์‹คํ–‰ ์ค€๋น„๋œ ๊ฒƒ๋“ค์„ ๋‹ด์•„ ๋‘๊ณ  ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด ๋Œ๋ฆฌ๋Š” ๊ตฌ์ฒด์  ๊ฐ์ฒด์ด๋ฉฐ ๊ทธ๊ฒƒ์ด ๋“  โ€œ๋ฉ”๋ชจ๋ฆฌโ€๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฌด์—‡์„ ๋‹ค์Œ์— ๋Œ๋ฆด์ง€ ์ ์€ ๊ด€๋ฆฌ์ž์˜ ์žฅ๋ถ€๋‹ค.

๊ด€๋ฆฌ์ž ์—ญํ• ์„ kernel์ด ์•„๋‹ˆ๋ผ user space์—์„œ ๋งก๋Š” ์…ˆ์ด๋ฉฐ, ๊ทธ ๋‚ด๋ถ€ ์ž๋ฃŒ๊ตฌ์กฐ์™€ ํ•œ ๋ฐ”ํ€ด์˜ ๋™์ž‘์€ 11์ ˆ์—์„œ ์ž์„ธํžˆ ๋ณธ๋‹ค.

  • ์ „ํ™˜์˜ ์ฃผ์ฒด: ์ฝ”๋“œ ์ž์‹ (await ์ง€์ ์—์„œ ์ž๋ฐœ์ ์œผ๋กœ)
  • ๋น„์šฉ: ์ž‘์Œ(์œ ์ € ๊ณต๊ฐ„, kernel ๊ฑฐ์น˜์ง€ ์•Š์Œ)
  • ๋ณ‘๋ ฌ: ๋ถˆ๊ฐ€๋Šฅ(๋‹จ์ผ thread๋ผ ๋งค ์ˆœ๊ฐ„ ํ•˜๋‚˜๋งŒ ์‹คํ–‰)
  • ์žฅ์ : ์ „ํ™˜ ์ง€์ ์ด ์ฝ”๋“œ์— await๋กœ ๋“œ๋Ÿฌ๋‚˜ ์ถ”์ ํ•˜๊ธฐ ์‰ฌ์›€
  • ํ•จ์ •: ํ•œ coroutine์ด ์–‘๋ณดํ•˜์ง€ ์•Š๊ณ  ์˜ค๋ž˜ ๋ถ™์žก์œผ๋ฉด event loop ์ „์ฒด๊ฐ€ ๋ฉˆ์ถค

coroutine์ด ๊ฐ€๋ฒผ์šด ์ด์œ 

์ผ๋ฐ˜ ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ๋˜๋ฉด ์Šคํƒ ํ”„๋ ˆ์ž„์ด ์Œ“์ด๊ณ  ๋๋‚˜๋ฉด ์‚ฌ๋ผ์ง„๋‹ค. ์ค‘๊ฐ„์— ๋ฉˆ์ท„๋‹ค ์žฌ๊ฐœํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

coroutine์€ โ€œ์–ด๋””๊นŒ์ง€ ์‹คํ–‰ํ–ˆ๋Š”์ง€, ๋‹ค์Œ์— ์–ด๋””์„œ ์žฌ๊ฐœํ• ์ง€, ์ง€์—ญ ์ƒํƒœ๊ฐ€ ๋ฌด์—‡์ธ์ง€โ€๋ฅผ ๊ฐ์ฒด ํ˜•ํƒœ์˜ ๊ฐ€๋ฒผ์šด ์‹คํ–‰ ์ƒํƒœ๋กœ ๋“ค๊ณ  ๋‹ค๋‹Œ๋‹ค.
๋•๋ถ„์— OS thread๋ณด๋‹ค ํ›จ์”ฌ ๋งŽ์€ ์ˆ˜๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค. thread๊ฐ€ ๊ฐ์ž ํฐ ์Šคํƒ์„ ๋ฏธ๋ฆฌ ์žก์•„๋‘๋Š” ๊ฒƒ๊ณผ ๋Œ€๋น„๋œ๋‹ค.

โ€œasynchronous ์ฒ˜๋ฆฌ๊ฐ€ OS์˜ context switch์™€ ๋ฌด์—‡์ด ๋‹ค๋ฅธ๊ฐ€โ€๋ผ๋Š” ์งˆ๋ฌธ์˜ ๋‹ต์ด ์•ž (A)ยท(B)์˜ ๋Œ€๋น„์— ์žˆ๋‹ค.
OS thread์˜ ์ „ํ™˜์€ kernel์ด ๊ฐ•์ œํ•˜๋Š” ๋ฌด๊ฑฐ์šด ์ „ํ™˜์ด๊ณ , event loop์˜ ์ „ํ™˜์€ ์ฝ”๋“œ๊ฐ€ await์—์„œ ์ž๋ฐœ์ ์œผ๋กœ ํ•˜๋Š” ๊ฐ€๋ฒผ์šด ์ „ํ™˜์ด๋‹ค.
๊ฐ™์€ โ€œ์ „ํ™˜โ€์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ์“ฐ์ง€๋งŒ ์ฃผ์ฒด์™€ ์‹œ์ , ๋น„์šฉ์ด ๋‹ค๋ฅด๋‹ค. ์—ฌ๊ธฐ์— goroutine๊นŒ์ง€ ๋”ํ•œ ์„ธ ๋ชจ๋ธ ๋น„๊ต๋Š” ๋‹ค์Œ ์ ˆ ๋ ํ‘œ์— ์žˆ๋‹ค.


10. ์ œ3์˜ ๊ธธ โ€” M:N runtime๊ณผ goroutine

์•ž์˜ ๋‘ ๋ชจ๋ธ์€ ๊ฐ๊ฐ ๋Œ€๊ฐ€๊ฐ€ ์žˆ๋‹ค.

  • OS thread๋Š” ์ง„์งœ ๋ณ‘๋ ฌ์„ ์ค„ ์ˆ˜ ์žˆ์ง€๋งŒ ์ „ํ™˜์ด ๋ฌด๊ฒ๊ณ , ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๋ฉด ๋ถ€๋‹ด์ด ์ปค์ง„๋‹ค.
  • event loop๋Š” ๊ฐ€๋ณ์ง€๋งŒ ๋‹จ์ผ thread๋ผ ๋ณ‘๋ ฌ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ด ๋‘˜์˜ ์žฅ์ , ๊ณง preemptive์˜ ๋ณ‘๋ ฌ๊ณผ cooperative์˜ ๊ฐ€๋ฒผ์›€์„ ํ•ฉ์น˜๋ ค๋Š” ๊ฒƒ์ด ์–ธ์–ด ๋Ÿฐํƒ€์ž„์ด ์‹คํ–‰ ํ๋ฆ„์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋Š” M:N ๋ชจ๋ธ์ด๋‹ค.

user thread๋ฅผ OS thread์— ๋ช‡ ๋Œ€ ๋ช‡์œผ๋กœ ์–น๋А๋ƒ๊ฐ€ threading model์„ ๊ฐ€๋ฅธ๋‹ค.

  • 1:1: user thread ํ•˜๋‚˜์— OS thread ํ•˜๋‚˜
    • Python threading, ๋ฆฌ๋ˆ…์Šค pthread๊ฐ€ ์ด ๋ฐฉ์‹์ด๋ผ ๋‘˜์˜ ๊ตฌ๋ถ„์ด ์ž˜ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š๋Š”๋‹ค.
  • N:1: ์—ฌ๋Ÿฌ user thread๋ฅผ OS thread ํ•˜๋‚˜์—
    • event loop์˜ coroutine๋“ค์ด ํ•ด๋‹น
    • kernel์ด ํ•˜๋‚˜๋กœ๋งŒ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ‘๋ ฌ์ด ์•ˆ ๋œ๋‹ค.
  • M:N: ์—ฌ๋Ÿฌ user thread๋ฅผ ์—ฌ๋Ÿฌ OS thread์—
    • ๋ณ‘๋ ฌ์ด ๋˜๋ฉฐ, ์•„๋ž˜ goroutine์ด ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ

Go์˜ goroutine์ด ๊ทธ M:N์˜ ๋Œ€ํ‘œ ์‚ฌ๋ก€๋‹ค.
์ˆ˜๋งŽ์€ goroutine์„ ์ ์€ ์ˆ˜์˜ OS thread ์œ„์— Go ๋Ÿฐํƒ€์ž„์ด ๋งคํ•‘ํ•ด ๋Œ๋ฆฐ๋‹ค. goroutine์ด ์ฑ„๋„ยทI/Oยท์‹œ์Šคํ…œ ์ฝœ์—์„œ ๋ง‰ํžˆ๋ฉด ๋Ÿฐํƒ€์ž„์ด ๊ทธ goroutine์„ ๋นผ๋‘๊ณ  ๊ฐ™์€ OS thread์— ๋‹ค๋ฅธ goroutine์„ ์˜ฌ๋ฆฐ๋‹ค.
์ด ์ „ํ™˜๋„ ์œ ์ € ๊ณต๊ฐ„์—์„œ ์ผ์–ด๋‚˜ ๋น„์šฉ์ด ์ž‘๊ณ  ๋Ÿฐํƒ€์ž„์€ ์—ฌ๋Ÿฌ OS thread๋ฅผ ๋™์‹œ์— ์“ฐ๋ฏ€๋กœ ์ง„์งœ ๋ณ‘๋ ฌ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์ „ํ™˜์˜ ์ฃผ์ฒด: ์–ธ์–ด ๋Ÿฐํƒ€์ž„(๋ง‰ํžˆ๋Š” ์ง€์  + ๋Ÿฐํƒ€์ž„์˜ ์„ ์ )
  • ๋น„์šฉ: ์ž‘์Œ(์œ ์ € ๊ณต๊ฐ„)
  • ๋ณ‘๋ ฌ: ๊ฐ€๋Šฅ(์—ฌ๋Ÿฌ OS thread์— ๋ถ„์‚ฐ)
  • ํ•œ ์ค„ ์š”์•ฝ: preemptive์˜ ์ง„์งœ ๋ณ‘๋ ฌ๊ณผ cooperative์˜ ๊ฐ€๋ฒผ์›€์„ ํ•ฉ์นœ ์ž๋ฆฌ

์ด ๋ฐœ์ƒ์€ Go๋งŒ์˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
Java์˜ virtual thread(Project Loom), ErlangยทElixir์˜ ๊ฒฝ๋Ÿ‰ process๋„ ๋Ÿฐํƒ€์ž„์ด ๊ฒฝ๋Ÿ‰ ์‹คํ–‰ ํ๋ฆ„์„ ์†Œ์ˆ˜์˜ OS thread์— ์‹ค์–ด ๋‚˜๋ฅด๋Š” ๊ฐ™์€ ๊ฐˆ๋ž˜๋‹ค.

threading model ๋น„๊ต

๋ชจ๋ธ๋ˆ„๊ฐ€ ์ „ํ™˜ํ•˜๋‚˜์–ธ์ œ๋น„์šฉ์ง„์งœ ๋ณ‘๋ ฌ?์ฝ”๋“œ ๋ชจ์Šต
OS thread (preemptive)kernel์ด ๊ฐ•์ œ๋กœOS๊ฐ€ ์ž„์˜ ์‹œ์ ์—ํผ(kernel)Oํ‰๋ฒ”ํ•œ synchronous ์ฝ”๋“œ
coroutine (cooperative)์ฝ”๋“œ๊ฐ€ ์Šค์Šค๋กœawait ์ง€์ ์—์„œ์ž‘์Œ(์œ ์ €)X (๋‹จ์ผ thread)async/await ํ‘œ์‹œ
goroutine (M:N)๋Ÿฐํƒ€์ž„์ด๋ง‰ํžˆ๋Š” ์ง€์  + ์„ ์ ์ž‘์Œ(์œ ์ €)Oํ‰๋ฒ”ํ•œ synchronous ์ฝ”๋“œ

11. ๋น„๋™๊ธฐ I/O์˜ ์‹ค์ œ ๋™์ž‘

๋‹จ์ผ thread event loop๊ฐ€ ์ˆ˜์ฒœ ๊ฐœ์˜ ์†Œ์ผ“์„ ๋™์‹œ์— ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ๊ฑด OS์˜ O multiplexing ๋•๋ถ„์ด๋‹ค. ์›๋ฆฌ๋Š” ๋‹จ์ˆœํ•˜๋‹ค.

  • ์ˆ˜์ฒœ ๊ฐœ์˜ ์†Œ์ผ“์„ ์ผ์ผ์ด blocking์œผ๋กœ ํ•˜๋‚˜์”ฉ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋Œ€์‹ ,
  • epoll_wait์œผ๋กœ OS์— โ€œ์ด ์ค‘ ์ค€๋น„๋œ fd๋งŒ ์•Œ๋ ค๋‹ฌ๋ผโ€๊ณ  ๋งก๊ธด๋‹ค.

์ด ํ†ต์ง€๋ฅผ ๋ฐ›์•„ ์ค€๋น„๋œ ๊ฒƒ๋งŒ ๊ณจ๋ผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒŒ event loop์ด๋‹ค. 9์ ˆ์—์„œ โ€œ์Šค์ผ€์ค„๋Ÿฌ ๊ฐ์ฒดโ€๋ผ ๋ถ€๋ฅธ ๊ทธ ๋‚ด๋ถ€๋ฅผ ๋œฏ์–ด๋ณด์ž.

์„ธ ์ž๋ฃŒ๊ตฌ์กฐ์™€ ์ž…๋ ฅ์›

๋จผ์ € ์šฉ์–ด๋ฅผ ๋งž์ถ”์ž. ์„ธ ๊ตฌ์กฐ์— ๋‹ด๊ธฐ๋Š” ๊ฑด coroutine ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ์ฝœ๋ฐฑ(asyncio ๋‚ด๋ถ€์—์„  Handle)์ด๋‹ค. ์ด ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•˜๋ฉด ๋ฌถ์ธ coroutine์ด ๋‹ค์Œ await๊นŒ์ง€ ์žฌ๊ฐœ๋œ๋‹ค.
ํŽธ์˜์ƒ โ€œcoroutine์ด ํŒŒํ‚น๋๋‹คโ€๊ณ  ๋งํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ ๋“  ๊ฑด ๊ทธ ์žฌ๊ฐœ ์ฝœ๋ฐฑ ๊ณง continuation์ด๋‹ค.

event loop์€ ์ด ์ฝœ๋ฐฑ๋“ค์„ ์„ธ ์ž๋ฃŒ๊ตฌ์กฐ์— ๋‚˜๋ˆ  ๋‹ด๋Š”๋‹ค. ์ฝœ๋ฐฑ์ด โ€œ์–ธ์ œ ์‹คํ–‰๋ผ์•ผ ํ•˜๋‚˜โ€๋กœ ๊ฐˆ๋ฆฐ๋‹ค.

  • ready ํ: ์ง€๊ธˆ ๋‹น์žฅ ์‹คํ–‰ํ•  ์ฝœ๋ฐฑ์˜ ๋Œ€๊ธฐ์—ด (์ฆ‰์‹œ ์‹คํ–‰)
  • timer heap: ์‹œ๊ฐ์ˆœ์œผ๋กœ ์ •๋ ฌ๋œ ์˜ˆ์•ฝ. ๋งจ ์•ž์ด ๊ฐ€์žฅ ์ด๋ฅธ ์˜ˆ์•ฝ (์‹œ๊ฐ ๋„๋ž˜)
  • selector: epoll์„ ๊ฐ์‹ผ ๋ž˜ํผ. โ€œ์–ด๋–ค fd๋ฅผ ๊ฐ์‹œ ์ค‘์ด๊ณ  ์ค€๋น„๋˜๋ฉด ์–ด๋–ค ์ฝœ๋ฐฑ์„ ๊นจ์šฐ๋‚˜โ€์˜ ๋งคํ•‘์„ ๋“ ๋‹ค (fd ์ค€๋น„)

์ฝœ๋ฐฑ์ด ์ด ์„ธ ๊ตฌ์กฐ์— ์ฒ˜์Œ ๋“ค์–ด๊ฐ€๋Š” primitive ๊ฒฝ๋กœ๋Š” ์…‹๋ฟ์ด๊ณ , ๋‚˜๋จธ์ง€ API๋Š” ์ „๋ถ€ ๊ทธ ์œ„์— ์–นํ˜€ ์žˆ๋‹ค.

primitive์ฒ˜์Œ ๋†“์ด๋Š” ๊ณณready ํ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ณ„๊ธฐ
loop.call_soon(cb)ready ํ ์งํ–‰์ฆ‰์‹œ. ๋‹ค์Œ ๋ฐ”ํ€ด์— ์‹คํ–‰
loop.call_later(d, cb) ยท call_attimer heap๊ทธ ์‹œ๊ฐ์ด ์ง€๋‚˜๋ฉด ์ด๋™
loop.add_reader/add_writer(fd, cb)selectorepoll์ด ๊ทธ fd ์ค€๋น„๋ฅผ ์•Œ๋ฆฌ๋ฉด ์ด๋™

ready ํ๋กœ ์งํ–‰ํ•˜๋Š” ๊ฑด call_soon๋ฟ์ด๋‹ค.
timer heap๊ณผ selector๋Š” ๋Œ€๊ธฐ์†Œ๋ผ, ๊ฑฐ๊ธฐ ๋“  ์ฝœ๋ฐฑ์€ โ€œ์‹œ๊ฐ ๋„๋ž˜โ€๋‚˜ โ€œfd ์ค€๋น„โ€๋ผ๋Š” ๊ณ„๊ธฐ๊ฐ€ ์žˆ์–ด์•ผ ready ํ๋กœ ์˜ฎ๊ฒจ์ง„๋‹ค.

์ƒ์œ„ API๋Š” ์ „๋ถ€ ์ด ์…‹์œผ๋กœ ํ™˜์›๋œ๋‹ค.

  • asyncio.create_task(coro) โ†’ ์ฒซ ์Šคํ…์„ call_soon โ†’ ready ํ ์งํ–‰
  • Future ์™„๋ฃŒ(set_result/set_exception) โ†’ ๊ฑธ๋ฆฐ done ์ฝœ๋ฐฑ์„ call_soon์œผ๋กœ โ†’ ready ํ
  • asyncio.sleep(d) โ†’ call_later โ†’ timer heap
  • await sock.recv()ยทreader.read()ยทwriter.drain() โ†’ add_reader/add_writer โ†’ selector
  • asyncio.wait_for(coro, t)ยทtimeout(t) โ†’ I/O๋Š” selector, ๋ฐ๋“œ๋ผ์ธ์€ timer heap์— ๋‘˜ ๋‹ค
  • call_soon_threadsafe(cb)ยทrun_in_executor ๊ฒฐ๊ณผ โ†’ ํƒ€ ์Šค๋ ˆ๋“œ๊ฐ€ ready ํ์— ๋„ฃ๊ณ  epoll_wait์„ ๊นจ์›€

call_soonยทcall_later์€ loop์˜ ๋ฉ”์„œ๋“œ

asyncio.call_soon ๊ฐ™์€ ๋ชจ๋“ˆ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ loop.call_soon(cb) ๊ผด๋กœ loop ๊ฐ์ฒด์— ๋ถ™์–ด ์žˆ๊ณ , coroutine์ด ์•„๋‹ˆ๋ผ ํ‰๋ฒ”ํ•œ ์ฝœ๋ฐฑ์„ ๋ฐ›๋Š”๋‹ค. ์ง์ ‘ ๋ถ€๋ฅผ ์ผ์€ ๋“œ๋ฌผ๊ณ  sleepยทtimeoutยทcreate_task๊ฐ€ ๋‚ด๋ถ€์—์„œ ๋ถ€๋ฅธ๋‹ค.

event loop ํ•œ ๋ฐ”ํ€ด๊ฐ€ ํ•˜๋Š” ์ผ

event loop์€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ํ•˜๋‚˜์˜ OS thread ์œ„์—์„œ while ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋ˆ๋‹ค. ํ•œ ๋ฐ”ํ€ด๋Š” ๋Œ€๋žต ์ด๋ ‡๋‹ค.

  1. timer heap ๋งจ ์•ž์„ ๋ณด๊ณ  ๋‹ค์Œ ์˜ˆ์•ฝ๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•œ๋‹ค. ์ด๊ฒŒ ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์ œํ•œ ์‹œ๊ฐ„์ด ๋œ๋‹ค.
  2. ๊ทธ ์‹œ๊ฐ„์„ ์ œํ•œ์œผ๋กœ selector๋ฅผ ๊ฒฝ์œ ํ•ด epoll_wait์„ ๋ถ€๋ฅธ๋‹ค.
    • ๊ฐ์‹œ ์ค‘์ธ fd๊ฐ€ ์ค€๋น„๋˜๋ฉด ์ฆ‰์‹œ ๊นจ๊ณ , ์•„๋ฌด๊ฒƒ๋„ ์—†์œผ๋ฉด timer ์‹œ๊ฐ๊นŒ์ง€ ์ž”๋‹ค โ†’ loop์ด ์œ ์ผํ•˜๊ฒŒ blocking๋˜๋Š” ์ง€์ 
    • ready ํ์— ์ด๋ฏธ ์ฝœ๋ฐฑ์ด ์žˆ์œผ๋ฉด ๋Œ€๊ธฐ ์—†์ด ์ง€๋‚˜๊ฐ„๋‹ค
  3. ๊นจ์–ด๋‚˜๋ฉด selector์˜ ์ค€๋น„๋œ fd ์ฝœ๋ฐฑ๊ณผ timer heap์˜ ๋งŒ๋ฃŒ๋œ ์ฝœ๋ฐฑ์„ ready ํ๋กœ ์˜ฎ๊ธด๋‹ค. call_soon์œผ๋กœ ์ด๋ฏธ ๋“ค์–ด์™€ ์žˆ๋˜ ์ฝœ๋ฐฑ๊นŒ์ง€, ์ด ์…‹์ด ์ด๋ฒˆ ๋ฐ”ํ€ด์— ์‹คํ–‰ํ•  ๋ชฉ๋ก์ด๋‹ค.
  4. ์ด๋ฒˆ ๋ฐ”ํ€ด ์‹œ์ž‘ ์‹œ์ ์— ํ์— ์žˆ๋˜ ์ฝœ๋ฐฑ์„ ํ•˜๋‚˜์”ฉ ์‹คํ–‰ํ•œ๋‹ค. ๊ฐ ์ฝœ๋ฐฑ์ด ๋ฌถ์ธ coroutine์„ ๋‹ค์Œ await๊นŒ์ง€ ๊ตด๋ฆฌ๊ณ , ์‹คํ–‰ ์ค‘ ์ƒˆ๋กœ ๊ฑธ๋ฆฐ ์ฝœ๋ฐฑ์€ ๋‹ค์Œ ๋ฐ”ํ€ด๋กœ ๋„˜๊ธด ๋’ค 1๋กœ ๋Œ์•„๊ฐ„๋‹ค.

๋ˆˆ์—ฌ๊ฒจ๋ณผ ์ ์€ timer์™€ I/O๊ฐ€ ํ•œ ๋ฒˆ์˜ epoll_wait์œผ๋กœ ํ†ตํ•ฉ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. fd๊ฐ€ ํ•˜๋‚˜๋„ ์ค€๋น„ ์•ˆ ๋ผ๋„ ๋‹ค์Œ timer ์‹œ๊ฐ์—” ๊นจ์•ผ ํ•˜๋ฏ€๋กœ, loop์€ โ€œ๋‹ค์Œ ์˜ˆ์•ฝ๊นŒ์ง€โ€๋ฅผ ์ œํ•œ ์‹œ๊ฐ„์œผ๋กœ ๋„˜๊ฒจ ๋‘ ์ด๋ฒคํŠธ์›์„ ํ•œ ๋Œ€๊ธฐ๋กœ ํ•ฉ์นœ๋‹ค.

%%{init: {'flowchart': {'curve': 'linear'}}}%%
flowchart LR
    CL(["call_later ยท ์ง€์—ฐ ์˜ˆ์•ฝ"])
    AR(["add_reader/writer ยท fd ๋“ฑ๋ก"])
    CS(["call_soon ยท ์ฆ‰์‹œ ์˜ˆ์•ฝ"])

    subgraph EL["event loop"]
        direction TB
        TH["timer heap"]
        SEL["selector = epoll_wait"]
        RQ["ready ํ"]
    end

    CL --> TH
    AR --> SEL
    CS --> RQ
    TH -->|๋‹ค์Œ ๋งˆ๊ฐ๊นŒ์ง€ timeout| SEL
    SEL -->|์ค€๋น„ fdยท๋งŒ๋ฃŒ timer ์ˆ˜๊ฑฐ| RQ
    RQ -->|์‹คํ–‰ ํ›„ ๋ฐ˜๋ณต| TH

    style EL fill:none,stroke:#495057,stroke-dasharray: 5 5
    classDef srcYellow fill:#ffec99,stroke:#f08c00
    classDef srcBlue fill:#a5d8ff,stroke:#1971c2
    classDef srcGreen fill:#b2f2bb,stroke:#2f9e44
    class CL,TH srcYellow
    class AR,SEL srcBlue
    class CS,RQ srcGreen

์ด ์„ธ ๊ตฌ์กฐ์˜ ํ˜‘๋ ฅ ๊ณจ๊ฒฉ์€ asyncio ์ „์šฉ์ด ์•„๋‹ˆ๋ผ reactor ํŒจํ„ด์œผ๋กœ, Node์˜ libuvยทGo ๋Ÿฐํƒ€์ž„ยทnginx๋„ ์ด๋ฆ„๋งŒ ๋‹ฌ๋ฆฌ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ์“ด๋‹ค.
timer๊ฐ€ heap์ด๋ƒ wheel์ด๋ƒ, ๋’ท๋‹จ์ด epoll์ด๋ƒ kqueue๋ƒ ์ •๋„๋งŒ ๊ฐˆ๋ฆฐ๋‹ค.

์ฝœ๋ฐฑ ํ•˜๋‚˜์˜ ์ด๋™ ์–‘์ƒ

์ด์ œ ์ฝœ๋ฐฑ ํ•˜๋‚˜๊ฐ€ ์—ฌ๋Ÿฌ await๋ฅผ ๊ฑฐ์น˜๋ฉฐ ์„ธ ๊ตฌ์กฐ ์‚ฌ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ์˜ฎ๊ฒจ ๋‹ค๋‹ˆ๋Š”์ง€ ๋”ฐ๋ผ๊ฐ€ ๋ณด์ž.

async def handle(reader, writer):
    data = await reader.read(100)   # โ‘  ์ฝ๊ธฐ I/O ๋Œ€๊ธฐ
    await asyncio.sleep(0.5)        # โ‘ก ์‹œ๊ฐ„ ๋Œ€๊ธฐ
    writer.write(process(data))
    await writer.drain()            # โ‘ข ์“ฐ๊ธฐ I/O ๋Œ€๊ธฐ

asyncio.create_task(handle(...))๋กœ ๋„์šฐ๋ฉด ๊ทธ continuation์ด ์ด๋ ‡๊ฒŒ ์ด๋™ํ•œ๋‹ค.

  1. ์‹œ์ž‘: ์ฒซ ์Šคํ… ์ฝœ๋ฐฑ์ด call_soon์œผ๋กœ ready ํ์— ์˜ฌ๋ผ๊ฐ€ ์‹คํ–‰๋˜๊ณ , await reader.read(100)์—์„œ ๋ฉˆ์ถ˜๋‹ค.
  2. selector: read๊ฐ€ ์†Œ์ผ“ fd๋ฅผ selector์— ๋“ฑ๋กํ•˜๋ฉฐ continuation์„ ํŒŒํ‚นํ•œ๋‹ค. epoll์ด โ€œ๊ทธ fd ์ฝ๊ธฐ ์ค€๋น„โ€๋ฅผ ์•Œ๋ฆฌ๋ฉด ์ฝœ๋ฐฑ์ด ready ํ๋กœ ์ด๋™ํ•ด ์‹คํ–‰๋˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์€ ๋’ค await asyncio.sleep(0.5)์—์„œ ๋ฉˆ์ถ˜๋‹ค.
  3. timer heap: sleep์ด call_later๋กœ 0.5์ดˆ ๋’ค continuation์„ timer heap์— ๋„ฃ๋Š”๋‹ค. 0.5์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด ๋งŒ๋ฃŒ๋œ ์ฝœ๋ฐฑ์ด ready ํ๋กœ ์ด๋™ํ•ด ์‹คํ–‰๋˜๊ณ , writer.write() ๋’ค await writer.drain()์—์„œ ๋ฉˆ์ถ˜๋‹ค.
  4. selector: ์“ฐ๊ธฐ ๋ฒ„ํผ๊ฐ€ ์ฐจ ์žˆ์œผ๋ฉด drain์ด fd๋ฅผ ์“ฐ๊ธฐ ๊ฐ์‹œ๋กœ selector์— ๋“ฑ๋กํ•˜๊ณ  ๋Œ€๊ธฐํ•œ๋‹ค(์—ฌ์œ ๊ฐ€ ์žˆ์œผ๋ฉด ์•ˆ ๋ฉˆ์ถ”๊ณ  ํ†ต๊ณผ). โ€œ์“ฐ๊ธฐ ๊ฐ€๋Šฅโ€ ์•Œ๋ฆผ์— ์ฝœ๋ฐฑ์ด ready ํ๋กœ ์ด๋™ํ•ด ์žฌ๊ฐœ๋˜๊ณ  coroutine์ด ๋๋‚œ๋‹ค.

์ด๋™ ๊ฒฝ๋กœ๋งŒ ๋ฝ‘์œผ๋ฉด ready ํ โ†’ selector โ†’ ready ํ โ†’ timer heap โ†’ ready ํ โ†’ selector โ†’ ready ํ๋‹ค.
๋งค๋ฒˆ โ€œ๋Œ€๊ธฐ์†Œ์— ํŒŒํ‚น โ†’ ๊ณ„๊ธฐ(fd ์ค€๋น„ยท์‹œ๊ฐ ๋„๋ž˜) โ†’ ready ํ๋กœ ์ด๋™ โ†’ ์‹คํ–‰โ€์„ ๋ฐ˜๋ณตํ•˜๊ณ , ์‹คํ–‰์€ ์–ธ์ œ๋‚˜ ready ํ๋ฅผ ๊ฑฐ์นœ๋‹ค.

epoll๊ณผ event loop์˜ ๋ถ„์—…

์ •๋ฆฌํ•˜๋ฉด asynchronous I/O๋Š” ๋‘ ์ธต์˜ ์กฐํ•ฉ์ด๋‹ค.

  1. OS์˜ ์ค€๋น„์™„๋ฃŒ ํ†ต์ง€(epoll): ์–ด๋–ค fd๊ฐ€ ์ค€๋น„๋๋Š”์ง€ ์ปค๋„์ด ์•Œ๋ ค์ค€๋‹ค.
  2. cooperative yield(await): coroutine์ด ๋Œ€๊ธฐ ์ง€์ ์—์„œ ์‹คํ–‰๊ถŒ์„ ๋„˜๊ฒจ, ๊ทธ๋™์•ˆ loop์ด ์ค€๋น„๋œ ๋‹ค๋ฅธ ์ฝœ๋ฐฑ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๊ทธ๋ž˜์„œ โ€œloop์€ epoll์ด ์ค€ fd๋งŒ ์Šค์ผ€์ค„๋งํ•˜๋Š” ์–‡์€ ์ธต ์•„๋‹ˆ๋ƒโ€๋Š” ์ ˆ๋ฐ˜๋งŒ ๋งž๋‹ค.
์ˆ˜์ฒœ fd๋ฅผ ๊ฐ์‹œํ•˜๋Š” ๋ฌด๊ฑฐ์šด ์ผ์€ ์ปค๋„(epoll)์ด ๋Œ€์‹  ์ง€๊ณ , loop์€ busy polling ์—†์ด epoll_wait์—์„œ ์ž๋‹ค ๊นจ๋ฉด ๋œ๋‹ค.

๋Œ€์‹  loop์ด ์ง์ ‘ ์ง€๋Š” ๋ชซ๋„ ๋ถ„๋ช…ํ•˜๋‹ค

  • fd์™€ ์ฝœ๋ฐฑ์˜ ๋งคํ•‘
  • timer heap ์œ ์ง€
  • await๋กœ ๋ฉˆ์ถ˜ coroutine์˜ ์ค‘๋‹จยท์žฌ๊ฐœ
  • ready ํ ์†Œ์ง„

์–ด๋ ค์šด ๊ฐ์‹œ๋Š” ์ปค๋„์— ๋งก๊ธฐ๊ณ  ์ž์‹ ์€ โ€œ๋ˆ„๊ตฌ๋ฅผ ์–ธ์ œ ๊นจ์šธ์ง€โ€๋งŒ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ„์—…์ด๋‹ค.

synchronous + non-blocking polling๊ณผ์˜ ์ฐจ์ด

8์ ˆ์—์„œ ๋ณธ โ€œnon-blocking ์†Œ์ผ“์„ ์ง์ ‘ ๋ฐ˜๋ณต ํ™•์ธโ€ํ•˜๋Š” ๋ฐฉ์‹์€ ์ค€๋น„๋๋Š”์ง€ ๊ณ„์† ๋ฌผ์–ด๋ณด๋А๋ผ CPU๋ฅผ ํƒœ์šด๋‹ค(busy polling). epoll์€ โ€œ์ค€๋น„๋˜๋ฉด ์•Œ๋ ค์ฃผ๊ฒ ๋‹คโ€๋Š” ๊ฐ์‹œ ์—ญํ• ์ด๋ผ ๊ทธ ๋‚ญ๋น„๊ฐ€ ์—†๋‹ค. ๋‹ค๋งŒ epoll ์ž์ฒด๋Š” ํ†ต์ง€๋งŒ ํ•  ๋ฟ, ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” recv() ํ˜ธ์ถœ์€ ์—ฌ์ „ํžˆ ํ•„์š”ํ•˜๋‹ค.

์—ฌ๊ธฐ์„œ ์ธต์œ„๋ฅผ ๋‚˜๋ˆ ์•ผ ํ•œ๋‹ค. ์ค€๋น„๋œ ์ž์›์— non-blocking recv()๋ฅผ ๋ถ€๋ฅด๋Š” ์ €์ˆ˜์ค€ ํ˜ธ์ถœ ์ž์ฒด๋Š” ๊ทธ ์ž๋ฆฌ์„œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์œผ๋‹ˆ 8์ ˆ์˜ synchronous + non-blocking์ด๊ณ , busy polling์˜ ๋˜๋ฌป๋Š” ๋‚ญ๋น„๊ฐ€ ์—†๋Š” ๊ฑด sync/async ๋•Œ๋ฌธ์ด ์•„๋‹ˆ๋ผ epoll ํ†ต์ง€ ๋•์ด๋‹ค.

asyncio๋Š” ์ด ์ €์ˆ˜์ค€ ๋ฉ”์ปค๋‹ˆ์ฆ˜(epoll ํ†ต์ง€ + non-blocking recv())์„ event loop๋กœ ๊ฐ์‹ธ, ํ”„๋กœ๊ทธ๋ž˜๋จธ์—๊ฒŒ๋Š” await๋กœ ๋“ฑ๋กํ•˜๊ณ  ๋‚˜์ค‘์— ๋ฐ›๋Š” asynchronous + non-blocking์œผ๋กœ ๋ณด์ด๊ฒŒ ์ถ”์ƒํ™”ํ•œ๋‹ค.


12. ์ง„์งœ ๋ณ‘๋ ฌ์€ ์–ธ์ œ ๊ฐ€๋Šฅํ•œ๊ฐ€

์ง„์งœ ๋ณ‘๋ ฌ์€ ์—ฌ๋Ÿฌ ์ฝ”์–ด์—์„œ ์—ฌ๋Ÿฌ ์‹คํ–‰ ํ๋ฆ„์ด ์‹ค์ œ๋กœ ๊ฐ™์€ ์ˆœ๊ฐ„์— ๋ฐฐ์น˜๋˜๋Š” ๊ฒƒ์ด๋‹ค. ํ•„์š”์กฐ๊ฑด์€ ๋‘ ๊ฐ€์ง€๋‹ค.

  1. ์ฝ”์–ด๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๊ฒƒ
  2. ์—ฌ๋Ÿฌ OS threadยทprocess๊ฐ€ ์‹ค์ œ๋กœ ๊ทธ ์ฝ”์–ด๋“ค์— ๋ฐฐ์น˜๋  ๊ฒƒ

ํ•ต์‹ฌ์€ ๋…ผ๋ฆฌ ์ฝ”์–ด์— ์˜ฌ๋ผ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์€ ์–ธ์ œ๋‚˜ OS thread๋ผ๋Š” ์ ์ด๋‹ค.
process๋„ ๊ฒฐ๊ตญ ๊ทธ ์•ˆ์˜ thread๊ฐ€ ์ฝ”์–ด์— ์˜ฌ๋ผ๊ฐ€๊ณ , user thread(coroutineยทgoroutine)๋Š” ์ž์‹ ์„ ํƒœ์›Œ์ค„ OS thread๊ฐ€ ์žˆ์–ด์•ผ ๋ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ณ‘๋ ฌ ์—ฌ๋ถ€๋Š” โ€œ๊ทธ ๋ชจ๋ธ์ด ์—ฌ๋Ÿฌ OS thread๋ฅผ ์—ฌ๋Ÿฌ ์ฝ”์–ด์— ์‹ค์ œ๋กœ ์˜ฌ๋ฆฌ๋А๋ƒโ€๋กœ ๊ฐˆ๋ฆฐ๋‹ค โ€” ๋‹จ์ผ OS thread์ธ event loop๋งŒ ๋ณ‘๋ ฌ์ด ์•ˆ ๋˜๊ณ , ์—ฌ๋Ÿฌ processยท์—ฌ๋Ÿฌ OS threadยทgoroutine์€ ๋ณ‘๋ ฌ์ด ๋œ๋‹ค.

๋™์‹œ์„ฑ์€ ๊ตฌ์กฐ๋ผ์„œ ์ฝ”์–ด๊ฐ€ ํ•˜๋‚˜์—ฌ๋„ ์„ฑ๋ฆฝํ•˜์ง€๋งŒ ๋ณ‘๋ ฌ์„ฑ์€ ๋ฌผ๋ฆฌ์  ์‹คํ–‰์ด๋ผ ์—ฌ๋Ÿฌ ์ฝ”์–ด์™€ ์—ฌ๋Ÿฌ OS ์‹คํ–‰ ๋‹จ์œ„๊ฐ€ ์‹ค์ œ๋กœ ํ•„์š”ํ•˜๋‹ค.


13. GIL โ€” Python์˜ ์ œ์•ฝ

Python thread๊ฐ€ ๋ณ‘๋ ฌ์—์„œ ์ œ์•ฝ์„ ๋ฐ›๋Š” ์ด์œ ๋Š” GIL(Global Interpreter Lock)์ด๋‹ค.
CPython ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—๋Š” ํ•œ ์ˆœ๊ฐ„์— ํ•˜๋‚˜์˜ thread๋งŒ Python ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ๋ง‰๋Š” ์ž ๊ธˆ์ด ์žˆ๋‹ค.

๊ฒฐ๊ณผ๋Š” ๋‘ ๊ฐ€์ง€๋‹ค.

  • thread๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“ค์–ด๋„ ์ˆœ์ˆ˜ Python CPU ์—ฐ์‚ฐ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ๋งŒ ๋ˆ๋‹ค. ๊ทธ๋ž˜์„œ CPU-bound ์ž‘์—…์„ thread๋กœ ๋‚˜๋ˆ ๋„ ๋นจ๋ผ์ง€์ง€ ์•Š๊ณ , ์ „ํ™˜ ๋น„์šฉ ํƒ“์— ์˜คํžˆ๋ ค ๋А๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ˜๋ฉด I/O ๋Œ€๊ธฐ ์ค‘์—๋Š” GIL์ด ํ’€๋ฆฐ๋‹ค. ๋„คํŠธ์›ŒํฌยทํŒŒ์ผ ๋Œ€๊ธฐ, ์ผ๋ถ€ C ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜ธ์ถœ ๊ตฌ๊ฐ„์—์„œ๋Š” ๋‹ค๋ฅธ thread๊ฐ€ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ I/O-bound ์ž‘์—…์—๋Š” Python thread๋„ ์ถฉ๋ถ„ํžˆ ์“ธ๋ชจ ์žˆ๋‹ค.

ํ”ํ•œ ์˜คํ•ด

โ€œPython์€ ๋™์‹œ์„ฑ์ด ์•ˆ ๋œ๋‹คโ€๋Š” ํ‹€๋ฆฐ ๋ง์ด๋‹ค. ์ •ํ™•ํžˆ๋Š” **โ€œCPython์—์„œ ์ˆœ์ˆ˜ Python CPU ์—ฐ์‚ฐ์„ thread๋กœ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ๋ฐ ์ œ์•ฝ์ด ์žˆ๋‹คโ€**๋Š” ๋œป์ด๋‹ค. I/O ๋™์‹œ์„ฑ์€ thread๋กœ๋„, asyncio๋กœ๋„ ์ž˜ ๋œ๋‹ค. CPU ๋ณ‘๋ ฌ์ด ํ•„์š”ํ•˜๋ฉด multiprocessing์œผ๋กœ ๋ณ„๋„ process๋ฅผ ๋„์›Œ GIL์„ ์šฐํšŒํ•˜๊ฑฐ๋‚˜ ๋„ค์ดํ‹ฐ๋ธŒ ํ™•์žฅ์œผ๋กœ ๊ฐ„๋‹ค.

GIL์€ CPython์˜ ์‚ฌ์ •์ผ ๋ฟ ๋ณดํŽธ ๋ฒ•์น™์ด ์•„๋‹ˆ๋‹ค.
GoยทJavaยทC++ ๊ฐ™์€ ์–ธ์–ด์—๋Š” GIL์ด ์—†์–ด์„œ thread๋‚˜ goroutine์ด ์—ฌ๋Ÿฌ ์ฝ”์–ด์—์„œ ๊ณง๋ฐ”๋กœ ๋ณ‘๋ ฌ๋กœ ๋ˆ๋‹ค. Go์˜ goroutine์ด CPU-bound์—๋„ ๊ฐ•ํ•œ ์ด์œ ๊ฐ€ ์—ฌ๊ธฐ์— ์žˆ๋‹ค.
Python๋„ 3.13๋ถ€ํ„ฐ GIL์„ ๋„๋Š” ์‹คํ—˜์  ๋นŒ๋“œ๋ฅผ ๋„์ž…ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์œผ๋‚˜, ์•„์ง ๊ธฐ๋ณธ์€ GIL์ด ์žˆ๋Š” ๋ชจ๋ธ์ด๋‹ค.

GIL์ด ์™œ ํ•„์š”ํ•œ์ง€(์ฐธ์กฐ ์นด์šดํŒ…)์™€ ์ปค๋„๊ณผ ๋ฌด๊ด€ํ•œ ๊ทธ ๋‚ด๋ถ€ ๋™์ž‘์€ Python GIL์—์„œ ๋‹ค๋ฃฌ๋‹ค.


14. ๊ฒฝ์Ÿ ์กฐ๊ฑด๊ณผ ๋™๊ธฐํ™”

๋™์‹œ์„ฑ์—๋Š” ๋Œ€๊ฐ€๊ฐ€ ๋”ฐ๋ฅธ๋‹ค. ์—ฌ๋Ÿฌ ์‹คํ–‰ ํ๋ฆ„์ด ๊ฐ™์€ ์ž์›(๋ณ€์ˆ˜ยทํŒŒ์ผ)์„ ๋™์‹œ์— ๊ฑด๋“œ๋ฆฌ๋ฉด, ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊ธด๋‹ค.
์ด๋ฅผ race condition์ด๋ผ ํ•œ๋‹ค.

๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ณต์œ  ์ž์›์— ๋™์‹œ์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ์ˆœ์„œ๋ฅผ ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
lock, mutex, semaphore ๊ฐ™์€ ๋™๊ธฐํ™” ๋„๊ตฌ๋กœ critical section์„ ์ง๋ ฌํ™”ํ•œ๋‹ค. ์…‹์€ ๋ชฉ์ ์€ ๊ฐ™์ง€๋งŒ ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋‹ค.

๋„๊ตฌ๋™์‹œ ํ—ˆ์šฉ์†Œ์œ ๊ถŒํ•ต์‹ฌ
lock๋ณดํ†ต 1๊ตฌํ˜„๋งˆ๋‹ค์ž ๊ธˆ์˜ ์ด์นญ์ด์ž ๊ธฐ๋ณธ ์ƒํ˜ธ ๋ฐฐ์ œ
mutex1์žˆ์Œ (์ž ๊ทผ ํ๋ฆ„๋งŒ ํ•ด์ œ)critical section์„ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ
semaphoreN์—†์Œ๋™์‹œ ์ ‘๊ทผ์„ N๊ฐœ๋กœ ์ œํ•œ, ์‹ ํ˜ธ ์ „๋‹ฌ

mutex๋Š” โ€œ์†Œ์œ ๊ถŒ ์žˆ๋Š” 1๊ฐœ์งœ๋ฆฌโ€(ํ™”์žฅ์‹ค ์—ด์‡  ํ•˜๋‚˜), semaphore๋Š” โ€œ์†Œ์œ ๊ถŒ ์—†๋Š” N๊ฐœ์งœ๋ฆฌโ€(์ฃผ์ฐจ์žฅ ๋นˆ์ž๋ฆฌ N๊ฐœ)๋กœ ๊ธฐ์–ตํ•˜๋ฉด ์‰ฝ๋‹ค.
๊ตฌ์ฒด์  API๋Š” ์–ธ์–ดยท๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค.

์šฉ์–ด๊ฐ€ ๋А์Šจํ•œ ์ด์œ  โ€” "lock"์€ ํ†ต์นญ์ด๋‹ค

์‹ค๋ฌด์—์„œ๋Š” lock๊ณผ mutex๋ฅผ ๊ฑฐ์˜ ํ˜ผ์šฉํ•œ๋‹ค. โ€œlock์„ ๊ฑด๋‹คโ€๊ณ  ํ•˜๋ฉด ๋ณดํ†ต mutex๋ฅผ ๋œปํ•˜์ง€๋งŒ, โ€œlockโ€์ด๋ผ๋Š” ์ด๋ฆ„ ์ž์ฒด๋Š” ์†Œ์œ ๊ถŒ ์œ ๋ฌด๋ฅผ ํŠน์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ โ€œLockโ€์ด๋ผ๋„ ๊ตฌํ˜„๋งˆ๋‹ค ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋‹ค.

  • Go sync.Mutex: ์ด๋ฆ„๋„ Mutex๊ณ  ์†Œ์œ ๊ถŒ ์žˆ๋Š” ์ง„์งœ mutex๋‹ค(์ž ๊ทผ goroutine๋งŒ ํ•ด์ œ).
  • Python threading.Lock: ์ด๋ฆ„์€ Lock์ด์ง€๋งŒ ์†Œ์œ ๊ถŒ์ด ์—†๋‹ค(๋‹ค๋ฅธ thread๊ฐ€ release ๊ฐ€๋Šฅ). ์—„๋ฐ€ํžˆ๋Š” binary semaphore์— ๊ฐ€๊น๊ณ , ์†Œ์œ ๊ถŒ ์žˆ๋Š” ๋ฒ„์ „์€ threading.RLock์ด๋‹ค.

์ •ํ™•ํžˆ ๊ฐ€๋ฅด๋ ค๋ฉด ์†Œ์œ ๊ถŒ ์œ ๋ฌด์™€ ๊ฐœ์ˆ˜ ๋‘ ๊ฐ€์ง€๋ฅผ ๋ณธ๋‹ค.

์†Œ์œ ๊ถŒ๊ฐœ์ˆ˜์ด๋ฆ„
์žˆ์Œ1mutex
์—†์Œ1binary semaphore (ํ”ํžˆ ๊ทธ๋ƒฅ โ€œlockโ€)
์—†์ŒNcounting semaphore

โ€œ์†Œ์œ ๊ถŒ ์—†๋Š” ์ž ๊ธˆโ€์„ ์ฝ• ์ง‘๋Š” ์ •์‹ ์šฉ์–ด๊ฐ€ binary semaphore์ด๊ณ , ์‹ค๋ฌด์—์„  ์ด๊ฒƒ๋„ ๊ทธ๋ƒฅ โ€œlockโ€์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

ํŒŒ์ผ์— ๊ฑฐ๋Š” ์ž ๊ธˆ๋„ ๊ฐ™์€ ๊ฐˆ๋ž˜๋‹ค.
๋ฆฌ๋ˆ…์Šค์˜ flock์€ ์†Œ์œ ๊ถŒ ์žˆ๋Š” ์ƒํ˜ธ๋ฐฐ์ œ๋ผ mutex์˜ ์„ฑ์งˆ์„ ๊ฐ–์ง€๋งŒ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์˜ ์ž ๊ธˆ์ด๊ณ  ์†Œ์œ  ๋‹จ์œ„๊ฐ€ thread๊ฐ€ ์•„๋‹ˆ๋ผ ํ”„๋กœ์„ธ์Šค(ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ)๋‹ค. ๊ทธ๋ž˜์„œ ์ฃผ๋กœ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์ƒํ˜ธ๋ฐฐ์ œ(์˜ˆ: ์Šคํฌ๋ฆฝํŠธ ๋‹จ์ผ ์‹คํ–‰ ๋ณด์žฅ)์— ์“ฐ๋ฉฐ, โ€œํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋ฎคํ…์Šคโ€๋กœ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

๋ชจ๋ธ๋งˆ๋‹ค ์œ„ํ—˜๋„๊ฐ€ ๋‹ค๋ฅด๋‹ค.

  • ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ thread: ์œ„ํ—˜์ด ๊ฐ€์žฅ ํฌ๋‹ค. ์•„๋ฌด ๋•Œ๋‚˜ preemption๋˜๋ฏ€๋กœ ๋‘ thread๊ฐ€ ๊ฐ™์€ ๋ณ€์ˆ˜๋ฅผ ๋™์‹œ์— ๊ฑด๋“œ๋ฆฌ๊ธฐ ์‰ฝ๋‹ค. lock ๊ด€๋ฆฌ์™€ deadlock๊นŒ์ง€ ์‹ ๊ฒฝ ์จ์•ผ ํ•œ๋‹ค.
  • event loop: ๋‹จ์ผ thread๋ผ โ€œ๋‘˜์ด ๊ฐ™์€ ์ค„์„ ๋™์‹œ์— ์‹คํ–‰โ€ํ•˜๋Š” ๊ฒฝ์Ÿ์€ ์—†๋‹ค. ๋‹ค๋งŒ await๋กœ ์–‘๋ณดํ•˜๋Š” ์‚ฌ์ด์— ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ์ˆ˜ ์žˆ์–ด ์™„์ „ํžˆ ์ž์œ ๋กญ์ง€๋Š” ์•Š๋‹ค. ์ด await ๊ฒฝ๊ณ„๋ฅผ ๋ณดํ˜ธํ•  ๋•Œ๋Š” event loop๋ฅผ ๋ง‰๋Š” threading.Lock์ด ์•„๋‹ˆ๋ผ asyncio.Lock์„ ์“ด๋‹ค. ์ „ํ™˜ ์ง€์ ์ด await๋กœ ๋“œ๋Ÿฌ๋‚˜ ์ถ”์ ์€ ์‰ฝ๋‹ค.
  • ์—ฌ๋Ÿฌ process: ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ฒฉ๋ฆฌ๋ผ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ์Ÿ์€ ์—†๋‹ค. ๋Œ€์‹  ๊ณต์œ  ํŒŒ์ผยทDB ๊ฐ™์€ ์™ธ๋ถ€ ์ž์›์— ๋Œ€ํ•œ ๊ฒฝ์Ÿ์€ ์—ฌ์ „ํžˆ ์กด์žฌํ•œ๋‹ค.

Go์˜ ์ ‘๊ทผ โ€” channel

Go๋Š” lock์œผ๋กœ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง€ํ‚ค๋Š” ๋Œ€์‹ , channel์ด๋ผ๋Š” ํ†ต๋กœ๋กœ goroutine๋ผ๋ฆฌ ๊ฐ’์„ ์ฃผ๊ณ ๋ฐ›๊ฒŒ ๊ถŒํ•œ๋‹ค. ์œ ๋ช…ํ•œ ๊ฒฉ์–ธ์ด ์ด๋ฅผ ์••์ถ•ํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•ด์„œ ์†Œํ†ตํ•˜์ง€ ๋ง๊ณ , ์†Œํ†ตํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๋ผ.
(Donโ€™t communicate by sharing memory; share memory by communicating.)
Go์—๋„ sync.Mutex๋Š” ์žˆ์œผ๋‚˜, ๊ธฐ๋ณธ ๋ฐœ์ƒ์€ โ€œ๊ณต์œ  ์ƒํƒœ๋ฅผ ์—ฌ๋Ÿฟ์ด ๊ฑด๋“œ๋ฆฌ์ง€ ๋ง๊ณ  ๊ฐ’์„ ํ†ต๋กœ๋กœ ๋„˜๊ฒจ๋ผโ€๋‹ค.


15. ๋™์‹œ์„ฑ ์ธต์œ„ ํ•œ๋ˆˆ์—

์ง€๊ธˆ๊นŒ์ง€์˜ ์‹คํ–‰ ๋ชจ๋ธ์„ ํ•˜๋‚˜์˜ ์ง€๋„๋กœ ๋ชจ์œผ๋ฉด ๋‘ ์ถ•์œผ๋กœ ๊ฐˆ๋ฆฐ๋‹ค.
ํ•˜๋‚˜๋Š” ๋‹จ์œ„์˜ ๊ณ„์ธต(process โŠƒ OS thread โŠƒ user thread)์ด๊ณ , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” user thread๋ฅผ OS thread์— ์–น๋Š” ๋งคํ•‘(N:1 / M:N)์ด๋‹ค.

process (๋…๋ฆฝ ๋ฉ”๋ชจ๋ฆฌ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ์—ฌ๋Ÿฌ ๊ฐœ = ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ
  โ”” OS thread (๋ฉ”๋ชจ๋ฆฌ ๊ณต์œ ) โ”€โ”€ ์—ฌ๋Ÿฌ ๊ฐœ = ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ
      โ”” user thread (kernel์€ ๋ชจ๋ฆ„, ๋Ÿฐํƒ€์ž„ยท๋ฃจํ”„๊ฐ€ ๊ด€๋ฆฌ)
          โ”œ N:1 โ†’ event loop + coroutine
          โ”” M:N โ†’ goroutine / virtual thread

process โŠƒ OS thread โŠƒ user thread๋Š” ํฌํ•จ ๊ด€๊ณ„์ด๊ณ , N:1๊ณผ M:N์€ user thread ์ธต์—์„œ๋งŒ ๊ฐˆ๋ฆฐ๋‹ค. event loop์™€ goroutine์€ ๋ถ€๋ชจ-์ž์‹์ด ์•„๋‹ˆ๋ผ ํ˜•์ œ๋‹ค.

๋ชจ๋ธ๋Š˜๋ฆฌ๋Š” ๋‹จ์œ„๋ฉ”๋ชจ๋ฆฌkernel์ด ์•„๋‚˜์ „ํ™˜ยท์Šค์ผ€์ค„๋™์‹œ์„ฑ์ง„์งœ ๋ณ‘๋ ฌ๋Œ€ํ‘œ
๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑprocess์™„์ „ ๊ฒฉ๋ฆฌOkernel(์„ ์ )OOmultiprocessing
๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉOS thread๊ณต์œ Okernel(์„ ์ )OGIL ์—†์œผ๋ฉด Othreading, Java thread
event loopuser thread (N:1)๊ฐ™์€ OS thread 1๊ฐœXevent loop(ํ˜‘๋ ฅ, await)OXasyncio, JS
M:N ๋Ÿฐํƒ€์ž„user thread (M:N)๋Ÿฐํƒ€์ž„ ๊ด€๋ฆฌX๋Ÿฐํƒ€์ž„(ํ˜‘๋ ฅ + ์„ ์ )OOgoroutine, virtual thread

๋™์‹œ์„ฑ์€ ๋„ค ์ธต์œ„ ๋ชจ๋‘ O๋‹ค โ€” ๋„ท ๋‹ค โ€œ์—ฌ๋Ÿฌ ์ผ์„ ๊ฒน์ณ ๋‹ค๋ฃจ๋Š”โ€ ๋™์‹œ์„ฑ ๋„๊ตฌ๋‹ค.
๊ฐˆ๋ฆฌ๋Š” ๊ฑด ์ง„์งœ ๋ณ‘๋ ฌ์ด๋ฉฐ, N:1 event loop๋งŒ ๋‹จ์ผ OS thread๋ผ X๋‹ค.

์–ธ์–ด๋ณ„๋กœ ์ฃผ๋กœ ์–ด๋А ์ธต์œ„๋ฅผ ์“ฐ๋Š”์ง€ ๋ณด๋ฉด ์ด๋ ‡๋‹ค.

์–ธ์–ดprocessOS thread (๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ)N:1 event loopM:N
PythonOO (I/O ๋™์‹œ์„ฑ O, CPU ๋ณ‘๋ ฌ์€ GIL์ด ๋ง‰์Œ)O (asyncio)ํ‘œ์ค€์—” ์—†์Œ
Go๋“œ๋ฌพM:N์˜ ํ•˜๋ถ€โ€”O (goroutine)
Java๊ฐ€๋ŠฅO (๋ณ‘๋ ฌ)๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌO (virtual thread)
JS / Nodeworkerยทclusterworker_threadsO (ํ•ต์‹ฌ)โ€”
Erlang / Elixirโ€”ํ•˜๋ถ€โ€”O (๊ฒฝ๋Ÿ‰ ํ”„๋กœ์„ธ์Šค)

16. ์–ด๋–ค ๋„๊ตฌ๋ฅผ ์–ธ์ œ ์“ฐ๋‚˜

์ง€๊ธˆ๊นŒ์ง€์˜ ํ‹€ ์œ„์— ์‹ค์ œ ๋„๊ตฌ๋ฅผ ๋ฐฐ์น˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

๋„๊ตฌ์‹คํ–‰ ๋ชจ๋ธ์ฃผ๋Š” ๊ฒƒ์ ํ•ฉํ•œ ์ž‘์—…
Python threading / ThreadPoolExecutorOS thread(preemptive)๋™์‹œ์„ฑ (CPU ๋ณ‘๋ ฌ์€ GIL์ด ๋ง‰์Œ)I/O-bound, ์ ์€ ์ˆ˜์˜ ๋™์‹œ ์ž‘์—…
Python asyncio๋‹จ์ผ thread event loop(cooperative)๋™์‹œ์„ฑ๋Œ€๋Ÿ‰ I/O-bound(์ˆ˜์ฒœ-์ˆ˜๋งŒ ์—ฐ๊ฒฐ)
Python multiprocessing / ProcessPoolExecutor์—ฌ๋Ÿฌ process๋ณ‘๋ ฌCPU-bound
Go goroutine + channelM:N runtime๋™์‹œ์„ฑ + ๋ณ‘๋ ฌI/OยทCPU ๋‘˜ ๋‹ค

์„ ํƒ ๊ธฐ์ค€์€ ๋Œ€๋žต ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • CPU-bound์ธ๊ฐ€. ์—ฌ๋Ÿฌ ์ฝ”์–ด์— ์‹ค์ œ๋กœ ๋‚˜๋ˆ ์•ผ ํ•œ๋‹ค. Python์ด๋ฉด multiprocessingยท๋„ค์ดํ‹ฐ๋ธŒ ํ™•์žฅ, Go๋ฉด goroutine.
  • I/O-bound์ธ๋ฐ ๋™์‹œ ์ž‘์—…์ด ์ ์€๊ฐ€. thread pool๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค. ์ฝ”๋“œ๊ฐ€ ํ‰๋ฒ”ํ•œ synchronous์‹์ด๋ผ ์ž‘์„ฑํ•˜๊ธฐ ์‰ฝ๋‹ค.
  • I/O-bound์ธ๋ฐ ๋™์‹œ ์ž‘์—…์ด ์ˆ˜์ฒœ ๊ฐœ ์ด์ƒ์ธ๊ฐ€. event loop๋‚˜ goroutine. thread ์ˆ˜์ฒœ ๊ฐœ๋Š” ์ „ํ™˜ ๋น„์šฉ์ด ์ปค์ง„๋‹ค.
  • ๋‘˜์ด ์„ž์—ฌ ์žˆ๋Š”๊ฐ€. ์‹ค๋ฌด์—์„œ ํ”ํ•˜๋‹ค. ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์€ asyncio๋กœ ๊ฒน์น˜๊ณ , CPU ํ›„์ฒ˜๋ฆฌ๋Š” process pool๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , synchronous ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋ณ„๋„ thread๋กœ ์šฐํšŒํ•œ๋‹ค(asyncio.to_thread). Go์—์„œ๋Š” goroutine ํ•˜๋‚˜๋กœ ๋‘ ๊ฒฐ์„ ๋ชจ๋‘ ๋‹ค๋ฃฌ๋‹ค.