Python ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ(Lock, RLock, Semaphore, Condition Variables ํฌํจ)์ ๋ํ ์ฌ์ธต ๊ฐ์ด๋. ๋์์ฑ์ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ ์ ํผํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค.
Python ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ ๋ง์คํฐํ๊ธฐ: Lock, RLock, Semaphore ๋ฐ Condition Variables
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ ์์ญ์์ Python์ ์ฌ๋ฌ ์ค๋ ๋๋ฅผ ๊ด๋ฆฌํ๊ณ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. Lock, RLock, Semaphore, Condition Variables์ ๊ฐ์ ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ดํดํ๊ณ ํ์ฉํ๋ ๊ฒ์ ๊ฐ๋ ฅํ๊ณ ํจ์จ์ ์ธ ๋ฉํฐ์ค๋ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ์ค์ํฉ๋๋ค. ์ด ์ข ํฉ ๊ฐ์ด๋๋ ๊ฐ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์์ธํ ์ดํด๋ณด๊ณ , Python์์ ๋์์ฑ์ ๋ง์คํฐํ๋ ๋ฐ ๋์์ด ๋๋ ์ค์ ์ ์ธ ์์์ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํฉ๋๋ค.
์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๊ฐ ์ค์ํ ์ด์
๋ฉํฐ์ค๋ ๋ฉ์ ํตํด ํ๋ก๊ทธ๋จ์ ์ฌ๋ฌ ๋ถ๋ถ์ ๋์์ ์คํํ ์ ์์ผ๋ฉฐ, ํนํ I/O ๋ฐ์ด๋ ์์ ์์ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ณต์ ๋ฆฌ์์ค์ ๋ํ ๋์ ์ ๊ทผ์ ๊ฒฝ์ ์กฐ๊ฑด, ๋ฐ์ดํฐ ์์ ๋ฐ ๊ธฐํ ๋์์ฑ ๊ด๋ จ ๋ฌธ์ ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ ์ค๋ ๋ ์คํ์ ๋๊ธฐํํ๊ณ , ์ถฉ๋์ ๋ฐฉ์งํ๋ฉฐ, ์ค๋ ๋ ์์ ์ ๋ณด์ฅํ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ณต์ ์ํ ๊ณ์ข ์์ก์ ๋์์ ์ ๋ฐ์ดํธํ๋ ค๊ณ ์๋ํ๋ ์๋๋ฆฌ์ค๋ฅผ ์๊ฐํด ๋ณด์ญ์์ค. ์ ์ ํ ๋๊ธฐํ๊ฐ ์์ผ๋ฉด ํ ์ค๋ ๋๊ฐ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๋ณ๊ฒฝํ ๋ด์ฉ์ ๋ฎ์ด์ธ ์ ์์ด ์๋ชป๋ ์ต์ข ์์ก์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ ๊ตํต ํต์ ๊ด ์ญํ ์ ํ์ฌ ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง ์ฝ๋์ ์๊ณ ์์ญ์ ์ ๊ทผํ๋๋ก ๋ณด์ฅํ์ฌ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
์ ์ญ ์ธํฐํ๋ฆฌํฐ ์ ๊ธ (GIL)
ํ๋ฆฌ๋ฏธํฐ๋ธ์ ๋ํด ์์ธํ ์์๋ณด๊ธฐ ์ ์ Python์ ์ ์ญ ์ธํฐํ๋ฆฌํฐ ์ ๊ธ(GIL)์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. GIL์ ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง Python ์ธํฐํ๋ฆฌํฐ์ ์ ์ด๊ถ์ ๊ฐ์ง ์ ์๋๋ก ํ๋ ๋ฎคํ ์ค์ ๋๋ค. ์ด๋ ๋ค์ค ์ฝ์ด ํ๋ก์ธ์์์๋ Python ๋ฐ์ดํธ์ฝ๋์ ์ง์ ํ ๋ณ๋ ฌ ์คํ์ด ์ ํ๋จ์ ์๋ฏธํฉ๋๋ค. GIL์ด CPU ๋ฐ์ด๋ ์์ ์ ๋ณ๋ชฉ ํ์์ด ๋ ์ ์์ง๋ง, ์ค๋ ๋๊ฐ ๋๋ถ๋ถ์ ์๊ฐ์ ์ธ๋ถ ๋ฆฌ์์ค๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋ฐ ์ฌ์ฉํ๋ I/O ๋ฐ์ด๋ ์์ ์๋ ์ค๋ ๋ฉ์ด ์ฌ์ ํ ์ ์ฉํ ์ ์์ต๋๋ค. ๋ํ NumPy์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์ ์ ์ํด GIL์ ํด์ ํ์ฌ ์ง์ ํ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
1. Lock ํ๋ฆฌ๋ฏธํฐ๋ธ
Lock์ด๋?
Lock(๋ฎคํ ์ค๋ผ๊ณ ๋ ํจ)์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋๊ธฐํ ํ๋ฆฌ๋ฏธํฐ๋ธ์ ๋๋ค. ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง Lock์ ํ๋ํ ์ ์๋๋ก ํฉ๋๋ค. Lock์ ํ๋ํ๋ ค๋ ๋ค๋ฅธ ์ค๋ ๋๋ Lock์ด ํด์ ๋ ๋๊น์ง ๋ธ๋ก(๋๊ธฐ)๋ฉ๋๋ค. ์ด๋ ๊ณต์ ๋ฆฌ์์ค์ ๋ํ ๋ ์ ์ ์ธ ์ ๊ทผ์ ๋ณด์ฅํฉ๋๋ค.
Lock ๋ฉ์๋
- acquire([blocking]): Lock์ ํ๋ํฉ๋๋ค. If blocking์ด
True
(๊ธฐ๋ณธ๊ฐ)์ด๋ฉด Lock์ ์ฌ์ฉํ ์ ์์ ๋๊น์ง ์ค๋ ๋๊ฐ ๋ธ๋ก๋ฉ๋๋ค. If blocking์ดFalse
์ด๋ฉด ๋ฉ์๋๋ ์ฆ์ ๋ฐํ๋ฉ๋๋ค. Lock์ด ํ๋๋๋ฉดTrue
๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉดFalse
๋ฅผ ๋ฐํํฉ๋๋ค. - release(): Lock์ ํด์ ํ์ฌ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ํ๋ํ ์ ์๋๋ก ํฉ๋๋ค. ์ ๊ธ ํด์ ๋ Lock์ ๋ํด
release()
๋ฅผ ํธ์ถํ๋ฉดRuntimeError
๊ฐ ๋ฐ์ํฉ๋๋ค. - locked(): Lock์ด ํ์ฌ ํ๋๋์์ผ๋ฉด
True
๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉดFalse
๋ฅผ ๋ฐํํฉ๋๋ค.
์์: ๊ณต์ ์นด์ดํฐ ๋ณดํธ
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ณต์ ์นด์ดํฐ๋ฅผ ์ฆ๊ฐ์ํค๋ ์๋๋ฆฌ์ค๋ฅผ ์๊ฐํด ๋ณด์ญ์์ค. Lock์ด ์์ผ๋ฉด ๊ฒฝ์ ์กฐ๊ฑด์ผ๋ก ์ธํด ์ต์ข ์นด์ดํฐ ๊ฐ์ด ์๋ชป๋ ์ ์์ต๋๋ค.
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
with lock:
counter += 1
threads = []
for _ in range(5):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Final counter value: {counter}")
์ด ์์์์ with lock:
๋ฌธ์ ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง counter
๋ณ์์ ์ ๊ทผํ๊ณ ์์ ํ ์ ์๋๋ก ๋ณด์ฅํฉ๋๋ค. with
๋ฌธ์ ์์ธ๊ฐ ๋ฐ์ํ๋๋ผ๋ ๋ธ๋ก ์์ ์ Lock์ ์๋์ผ๋ก ํ๋ํ๊ณ ๋๋ ๋ ํด์ ํฉ๋๋ค. ์ด ๊ตฌ์กฐ๋ lock.acquire()
๋ฐ lock.release()
๋ฅผ ์๋์ผ๋ก ํธ์ถํ๋ ๊ฒ๋ณด๋ค ๋ ๊น๋ํ๊ณ ์์ ํ ๋์์ ์ ๊ณตํฉ๋๋ค.
์ค์ํ ๋น์
ํ ๋ฒ์ ํ ๋์ ์ฐจ๋๋ง ํตํํ ์ ์๋ ๋จ์ผ ์ฐจ์ ๋ค๋ฆฌ๋ฅผ ์์ํด ๋ณด์ญ์์ค. Lock์ ๋ค๋ฆฌ์ ๋ํ ์ ๊ทผ์ ํต์ ํ๋ ๋ฌธ์ง๊ธฐ์ ๊ฐ์ต๋๋ค. ์ฐจ๋(์ค๋ ๋)์ด ๊ฑด๋๊ณ ์ถ์ผ๋ฉด ๋ฌธ์ง๊ธฐ์ ํ๋ฝ์ ๋ฐ์์ผ ํฉ๋๋ค(Lock ํ๋). ํ ๋ฒ์ ํ ๋์ ์ฐจ๋๋ง ํ๋ฝ์ ๋ฐ์ ์ ์์ต๋๋ค. ์ฐจ๋์ด ๊ฑด๋๊ณ ๋๋ฉด(์๊ณ ์์ญ ์๋ฃ), ํ๋ฝ์ ํด์ ํ์ฌ(Lock ํด์ ) ๋ค๋ฅธ ์ฐจ๋์ด ๊ฑด๋๊ฐ ์ ์๋๋ก ํฉ๋๋ค.
2. RLock ํ๋ฆฌ๋ฏธํฐ๋ธ
RLock์ด๋?
RLock(์ฌ์ง์ Lock)์ ๋์ผํ ์ค๋ ๋๊ฐ ๋ธ๋ก๋์ง ์๊ณ Lock์ ์ฌ๋ฌ ๋ฒ ํ๋ํ ์ ์๋๋ก ํ๋ ๋ ๊ณ ๊ธ ์ ํ์ Lock์ ๋๋ค. ์ด๋ Lock์ ๋ณด์ ํ ํจ์๊ฐ ๋์ผํ Lock์ ํ๋ํด์ผ ํ๋ ๋ค๋ฅธ ํจ์๋ฅผ ํธ์ถํ๋ ์ํฉ์์ ์ ์ฉํฉ๋๋ค. ์ผ๋ฐ Lock์ ์ด๋ฌํ ์ํฉ์์ ๊ต์ฐฉ ์ํ๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค.
RLock ๋ฉ์๋
RLock์ ๋ฉ์๋๋ Lock๊ณผ ๋์ผํฉ๋๋ค: acquire([blocking])
, release()
, locked()
. ๊ทธ๋ฌ๋ ๋์์ ๋ค๋ฆ
๋๋ค. ๋ด๋ถ์ ์ผ๋ก RLock์ ๋์ผํ ์ค๋ ๋์ ์ํด ํ๋๋ ํ์๋ฅผ ์ถ์ ํ๋ ์นด์ดํฐ๋ฅผ ์ ์งํฉ๋๋ค. Lock์ ํ๋๋ ํ์๋งํผ release()
๋ฉ์๋๊ฐ ํธ์ถ๋ ๋๋ง ์์ ํ ํด์ ๋ฉ๋๋ค.
์์: RLock์ ์ฌ์ฉํ ์ฌ๊ท ํจ์
๊ณต์ ๋ฆฌ์์ค์ ์ ๊ทผํด์ผ ํ๋ ์ฌ๊ท ํจ์๋ฅผ ์๊ฐํด ๋ณด์ญ์์ค. RLock์ด ์์ผ๋ฉด ํจ์๋ ์ฌ๊ท์ ์ผ๋ก Lock์ ํ๋ํ๋ ค๊ณ ํ ๋ ๊ต์ฐฉ ์ํ์ ๋น ์ง ๊ฒ์ ๋๋ค.
import threading
lock = threading.RLock()
def recursive_function(n):
with lock:
if n <= 0:
return
print(f"Thread {threading.current_thread().name}: Processing {n}")
recursive_function(n - 1)
thread = threading.Thread(target=recursive_function, args=(5,))
thread.start()
thread.join()
์ด ์์์์ RLock
์ recursive_function
์ด ๋ธ๋ก๋์ง ์๊ณ Lock์ ์ฌ๋ฌ ๋ฒ ํ๋ํ ์ ์๋๋ก ํฉ๋๋ค. recursive_function
์ ๋ํ ๊ฐ ํธ์ถ์ Lock์ ํ๋ํ๊ณ , ๊ฐ ๋ฐํ์ Lock์ ํด์ ํฉ๋๋ค. Lock์ recursive_function
์ ๋ํ ์ด๊ธฐ ํธ์ถ์ด ๋ฐํ๋ ๋๋ง ์์ ํ ํด์ ๋ฉ๋๋ค.
์ค์ํ ๋น์
ํ์ฌ์ ๊ธฐ๋ฐ ํ์ผ์ ์ ๊ทผํด์ผ ํ๋ ๊ด๋ฆฌ์๋ฅผ ์์ํด ๋ณด์ญ์์ค. RLock์ ๊ด๋ฆฌ์๊ฐ ๋งค๋ฒ ์ฌ์ธ์ฆํ ํ์ ์์ด ํ์ผ๋ฃธ์ ๋ค๋ฅธ ์น์ ์ ์ฌ๋ฌ ๋ฒ ๋ค์ด๊ฐ ์ ์๋๋ก ํ๋ ํน๋ณํ ์ ๊ทผ ์นด๋์ ๊ฐ์ต๋๋ค. ๊ด๋ฆฌ์๋ ํ์ผ์ ์์ ํ ์ฌ์ฉํ๊ณ ํ์ผ๋ฃธ์ ๋ ๋ ํ์๋ง ์นด๋๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค.
3. Semaphore ํ๋ฆฌ๋ฏธํฐ๋ธ
Semaphore๋?
Semaphore๋ Lock๋ณด๋ค ๋ ์ผ๋ฐ์ ์ธ ๋๊ธฐํ ํ๋ฆฌ๋ฏธํฐ๋ธ์ ๋๋ค. ์ด๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฆฌ์์ค ์๋ฅผ ๋ํ๋ด๋ ์นด์ดํฐ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. ์ค๋ ๋๋ ์นด์ดํฐ๋ฅผ ๊ฐ์์์ผ(์์์ผ ๊ฒฝ์ฐ) ์ธ๋งํฌ์ด๋ฅผ ํ๋ํ๊ฑฐ๋, ์นด์ดํฐ๊ฐ ์์๊ฐ ๋ ๋๊น์ง ๋ธ๋ก๋ ์ ์์ต๋๋ค. ์ค๋ ๋๋ ์นด์ดํฐ๋ฅผ ์ฆ๊ฐ์์ผ ์ธ๋งํฌ์ด๋ฅผ ํด์ ํ๋ฉฐ, ์ด๋ ๋ธ๋ก๋ ์ค๋ ๋๋ฅผ ๊นจ์ธ ์ ์์ต๋๋ค.
Semaphore ๋ฉ์๋
- acquire([blocking]): ์ธ๋งํฌ์ด๋ฅผ ํ๋ํฉ๋๋ค. If blocking์ด
True
(๊ธฐ๋ณธ๊ฐ)์ด๋ฉด ์ธ๋งํฌ์ด ์นด์ดํธ๊ฐ 0๋ณด๋ค ์ปค์ง ๋๊น์ง ์ค๋ ๋๊ฐ ๋ธ๋ก๋ฉ๋๋ค. If blocking์ดFalse
์ด๋ฉด ๋ฉ์๋๋ ์ฆ์ ๋ฐํ๋ฉ๋๋ค. ์ธ๋งํฌ์ด๊ฐ ํ๋๋๋ฉดTrue
๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉดFalse
๋ฅผ ๋ฐํํฉ๋๋ค. ๋ด๋ถ ์นด์ดํฐ๋ฅผ 1 ๊ฐ์์ํต๋๋ค. - release(): ์ธ๋งํฌ์ด๋ฅผ ํด์ ํ๊ณ ๋ด๋ถ ์นด์ดํฐ๋ฅผ 1 ์ฆ๊ฐ์ํต๋๋ค. ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์ธ๋งํฌ์ด๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ค๋ฉด ๊ทธ์ค ํ๋๊ฐ ๊นจ์ด๋ฉ๋๋ค.
- get_value(): ๋ด๋ถ ์นด์ดํฐ์ ํ์ฌ ๊ฐ์ ๋ฐํํฉ๋๋ค.
์์: ๋ฆฌ์์ค์ ๋ํ ๋์ ์ ๊ทผ ์ ํ
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ํ ๋์ ์ฐ๊ฒฐ ์๋ฅผ ์ ํํ๋ ค๋ ์๋๋ฆฌ์ค๋ฅผ ์๊ฐํด ๋ณด์ญ์์ค. ์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ ์ ์๋ ์ค๋ ๋ ์๋ฅผ ์ ์ดํ ์ ์์ต๋๋ค.
import threading
import time
import random
semaphore = threading.Semaphore(3) # Allow only 3 concurrent connections
def database_access():
with semaphore:
print(f"Thread {threading.current_thread().name}: Accessing database...")
time.sleep(random.randint(1, 3)) # Simulate database access
print(f"Thread {threading.current_thread().name}: Releasing database...")
threads = []
for i in range(5):
t = threading.Thread(target=database_access, name=f"Thread-{i}")
threads.append(t)
t.start()
for t in threads:
t.join()
์ด ์์์์ ์ธ๋งํฌ์ด๋ 3์ผ๋ก ์ด๊ธฐํ๋์ด, ํ ๋ฒ์ 3๊ฐ์ ์ค๋ ๋๋ง ์ธ๋งํฌ์ด๋ฅผ ํ๋(๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผ)ํ ์ ์์์ ์๋ฏธํฉ๋๋ค. ๋ค๋ฅธ ์ค๋ ๋๋ ์ธ๋งํฌ์ด๊ฐ ํด์ ๋ ๋๊น์ง ๋ธ๋ก๋ฉ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ณผ๋ถํ๋ฅผ ๋ฐฉ์งํ๊ณ ๋์ ์์ฒญ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ๋ณด์ฅํฉ๋๋ค.
์ค์ํ ๋น์
ํ ์ด๋ธ ์๊ฐ ์ ํ๋ ์ธ๊ธฐ ์๋ ๋ ์คํ ๋์ ์์ํด ๋ณด์ญ์์ค. ์ธ๋งํฌ์ด๋ ๋ ์คํ ๋์ ์ข์ ์์ฉ ๋ฅ๋ ฅ๊ณผ ๊ฐ์ต๋๋ค. ํ ๋ฌด๋ฆฌ์ ์ฌ๋๋ค(์ค๋ ๋)์ด ๋์ฐฉํ๋ฉด, ์ถฉ๋ถํ ํ ์ด๋ธ์ด ์๋ค๋ฉด ์ฆ์ ์์ ์ ์์ต๋๋ค(์ธ๋งํฌ์ด ์นด์ดํธ๊ฐ ์์). ๋ชจ๋ ํ ์ด๋ธ์ด ์ฐจ์ง๋์ด ์๋ค๋ฉด, ํ ์ด๋ธ์ด ๋น์์ง ๋๊น์ง ๋๊ธฐ ๊ตฌ์ญ์์ ๊ธฐ๋ค๋ ค์ผ ํฉ๋๋ค(๋ธ๋ก). ํ ๊ทธ๋ฃน์ด ๋ ๋๋ฉด(์ธ๋งํฌ์ด ํด์ ), ๋ค๋ฅธ ๊ทธ๋ฃน์ด ์์ ์ ์์ต๋๋ค.
4. Condition Variable ํ๋ฆฌ๋ฏธํฐ๋ธ
Condition Variable์ด๋?
Condition Variable์ ์ค๋ ๋๊ฐ ํน์ ์กฐ๊ฑด์ด ์ฐธ์ด ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆด ์ ์๋๋ก ํ๋ ๋ ๊ณ ๊ธ ๋๊ธฐํ ํ๋ฆฌ๋ฏธํฐ๋ธ์
๋๋ค. ์ด๋ ํญ์ Lock(Lock
๋๋ RLock
)๊ณผ ์ฐ๊ด๋ฉ๋๋ค. ์ค๋ ๋๋ Condition Variable์์ ๋๊ธฐํ ์ ์์ผ๋ฉฐ, ์ฐ๊ด๋ Lock์ ํด์ ํ๊ณ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์กฐ๊ฑด์ ์๋ฆด ๋๊น์ง ์คํ์ ์ผ์ ์ค๋จํฉ๋๋ค. ์ด๋ ์์ฐ์-์๋น์ ์๋๋ฆฌ์ค ๋๋ ์ค๋ ๋๊ฐ ํน์ ์ด๋ฒคํธ์ ๋ฐ๋ผ ์กฐ์ ํด์ผ ํ๋ ์ํฉ์์ ์ค์ํฉ๋๋ค.
Condition Variable ๋ฉ์๋
- acquire([blocking]): ๋ด๋ถ Lock์ ํ๋ํฉ๋๋ค. ์ฐ๊ด๋ Lock์
acquire
๋ฉ์๋์ ๋์ผํฉ๋๋ค. - release(): ๋ด๋ถ Lock์ ํด์ ํฉ๋๋ค. ์ฐ๊ด๋ Lock์
release
๋ฉ์๋์ ๋์ผํฉ๋๋ค. - wait([timeout]): ๋ด๋ถ Lock์ ํด์ ํ๊ณ
notify()
๋๋notify_all()
ํธ์ถ์ ์ํด ๊นจ์ด๋ ๋๊น์ง ๋๊ธฐํฉ๋๋ค. Lock์wait()
๊ฐ ๋ฐํ๋๊ธฐ ์ ์ ๋ค์ ํ๋๋ฉ๋๋ค. ์ ํ์ timeout ์ธ์๋ ์ต๋ ๋๊ธฐ ์๊ฐ์ ์ง์ ํฉ๋๋ค. - notify(n=1): ์ต๋ n๊ฐ์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ฅผ ๊นจ์๋๋ค.
- notify_all(): ๋ชจ๋ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ฅผ ๊นจ์๋๋ค.
์์: ์์ฐ์-์๋น์ ๋ฌธ์
๊ณ ์ ์ ์ธ ์์ฐ์-์๋น์ ๋ฌธ์ ๋ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ํ๋ ์ด์์ ์์ฐ์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ํ๋ ์ด์์ ์๋น์๋ฅผ ํฌํจํฉ๋๋ค. ๊ณต์ ๋ฒํผ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ, ์์ฐ์์ ์๋น์๋ ๊ฒฝ์ ์กฐ๊ฑด์ ํผํ๊ธฐ ์ํด ๋ฒํผ์ ๋ํ ์ ๊ทผ์ ๋๊ธฐํํด์ผ ํฉ๋๋ค.
import threading
import time
import random
buffer = []
buffer_size = 5
condition = threading.Condition()
def producer():
global buffer
while True:
with condition:
if len(buffer) == buffer_size:
print("Buffer is full, producer waiting...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"Produced: {item}, Buffer: {buffer}")
condition.notify()
time.sleep(random.random())
def consumer():
global buffer
while True:
with condition:
if not buffer:
print("Buffer is empty, consumer waiting...")
condition.wait()
item = buffer.pop(0)
print(f"Consumed: {item}, Buffer: {buffer}")
condition.notify()
time.sleep(random.random())
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()
์ด ์์์์ condition
๋ณ์๋ ์์ฐ์์ ์๋น์ ์ค๋ ๋๋ฅผ ๋๊ธฐํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ฒํผ๊ฐ ๊ฐ๋ ์ฐจ๋ฉด ์์ฐ์๋ ๋๊ธฐํ๊ณ , ๋ฒํผ๊ฐ ๋น์ด ์์ผ๋ฉด ์๋น์๋ ๋๊ธฐํฉ๋๋ค. ์์ฐ์๊ฐ ๋ฒํผ์ ํญ๋ชฉ์ ์ถ๊ฐํ๋ฉด ์๋น์๋ฅผ ์๋ฆฌ๊ณ , ์๋น์๊ฐ ๋ฒํผ์์ ํญ๋ชฉ์ ์ ๊ฑฐํ๋ฉด ์์ฐ์๋ฅผ ์๋ฆฝ๋๋ค. with condition:
๋ฌธ์ Condition Variable๊ณผ ์ฐ๊ด๋ Lock์ด ์ฌ๋ฐ๋ฅด๊ฒ ํ๋๋๊ณ ํด์ ๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
์ค์ํ ๋น์
์์ฐ์(๊ณต๊ธ์ ์ฒด)๊ฐ ๋ฌผํ์ ๋ฐฐ์กํ๊ณ ์๋น์(๊ณ ๊ฐ)๊ฐ ๋ฌผํ์ ํฝ์ ํ๋ ์ฐฝ๊ณ ๋ฅผ ์์ํด ๋ณด์ญ์์ค. ๊ณต์ ๋ฒํผ๋ ์ฐฝ๊ณ ์ ์ฌ๊ณ ์ ๊ฐ์ต๋๋ค. Condition Variable์ ๊ณต๊ธ์ ์ฒด์ ๊ณ ๊ฐ์ด ํ๋์ ์กฐ์ ํ ์ ์๋๋ก ํ๋ ํต์ ์์คํ ๊ณผ ๊ฐ์ต๋๋ค. ์ฐฝ๊ณ ๊ฐ ๊ฐ๋ ์ฐจ๋ฉด ๊ณต๊ธ์ ์ฒด๋ ๊ณต๊ฐ์ด ์๊ธธ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค. ์ฐฝ๊ณ ๊ฐ ๋น์ด ์์ผ๋ฉด ๊ณ ๊ฐ์ ๋ฌผํ์ด ๋์ฐฉํ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค. ๋ฌผํ์ด ๋ฐฐ์ก๋๋ฉด ๊ณต๊ธ์ ์ฒด๋ ๊ณ ๊ฐ์๊ฒ ์๋ฆฝ๋๋ค. ๋ฌผํ์ด ํฝ์ ๋๋ฉด ๊ณ ๊ฐ์ ๊ณต๊ธ์ ์ฒด์ ์๋ฆฝ๋๋ค.
์ฌ๋ฐ๋ฅธ ํ๋ฆฌ๋ฏธํฐ๋ธ ์ ํ
ํจ๊ณผ์ ์ธ ๋์์ฑ ๊ด๋ฆฌ๋ฅผ ์ํด์๋ ์ ์ ํ ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ ํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๋ค์์ ์ ํ์ ๋์์ด ๋๋ ์์ฝ์ ๋๋ค:
- Lock: ๊ณต์ ๋ฆฌ์์ค์ ๋ํ ๋ ์ ์ ์ธ ์ ๊ทผ์ด ํ์ํ๊ณ ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง ์ ๊ทผํด์ผ ํ ๋ ์ฌ์ฉํฉ๋๋ค.
- RLock: ์ฌ๊ท ํจ์ ๋๋ ์ค์ฒฉ๋ ์๊ณ ์์ญ๊ณผ ๊ฐ์ด ๋์ผํ ์ค๋ ๋๊ฐ Lock์ ์ฌ๋ฌ ๋ฒ ํ๋ํด์ผ ํ ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํฉ๋๋ค.
- Semaphore: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ๋๋ ํน์ ์์ ์ ์ํํ๋ ์ค๋ ๋ ์ ์ ํ๊ณผ ๊ฐ์ด ๋ฆฌ์์ค์ ๋ํ ๋์ ์ ๊ทผ ์๋ฅผ ์ ํํด์ผ ํ ๋ ์ฌ์ฉํฉ๋๋ค.
- Condition Variable: ์์ฐ์-์๋น์ ์๋๋ฆฌ์ค ๋๋ ์ค๋ ๋๊ฐ ํน์ ์ด๋ฒคํธ์ ๋ฐ๋ผ ์กฐ์ ํด์ผ ํ ๋์ ๊ฐ์ด ์ค๋ ๋๊ฐ ํน์ ์กฐ๊ฑด์ด ์ฐธ์ด ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ ๋ ์ฌ์ฉํฉ๋๋ค.
์ผ๋ฐ์ ์ธ ํจ์ ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก
์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ด๋ ค์ธ ์ ์์ผ๋ฉฐ, ์ผ๋ฐ์ ์ธ ํจ์ ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์๋ ๊ฒ์ด ์ค์ํฉ๋๋ค:
- ๊ต์ฐฉ ์ํ (Deadlock): ๋ ๊ฐ ์ด์์ ์ค๋ ๋๊ฐ ์๋ก ๋ฆฌ์์ค๋ฅผ ํด์ ํ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ๋ฌด๊ธฐํ์ผ๋ก ๋ธ๋ก๋ ๋ ๋ฐ์ํฉ๋๋ค. ์ผ๊ด๋ ์์๋ก Lock์ ํ๋ํ๊ณ Lock ํ๋ ์ ํ์์์์ ์ฌ์ฉํ์ฌ ๊ต์ฐฉ ์ํ๋ฅผ ๋ฐฉ์งํ์ญ์์ค.
- ๊ฒฝ์ ์กฐ๊ฑด (Race Conditions): ํ๋ก๊ทธ๋จ์ ๊ฒฐ๊ณผ๊ฐ ์ค๋ ๋๊ฐ ์คํ๋๋ ์์ธก ๋ถ๊ฐ๋ฅํ ์์์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ๋ ๋ฐ์ํฉ๋๋ค. ๊ณต์ ๋ฆฌ์์ค๋ฅผ ๋ณดํธํ๊ธฐ ์ํด ์ ์ ํ ๋๊ธฐํ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฝ์ ์กฐ๊ฑด์ ๋ฐฉ์งํ์ญ์์ค.
- ๊ธฐ์ ์ํ (Starvation): ๋ฆฌ์์ค๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํจ์๋ ๋ถ๊ตฌํ๊ณ ์ค๋ ๋๊ฐ ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ์ ๋ฐ๋ณต์ ์ผ๋ก ๊ฑฐ๋ถ๋นํ ๋ ๋ฐ์ํฉ๋๋ค. ์ ์ ํ ์ค์ผ์ค๋ง ์ ์ฑ ์ ์ฌ์ฉํ๊ณ ์ฐ์ ์์ ์ญ์ ์ ํผํ์ฌ ๊ณต์ ์ฑ์ ๋ณด์ฅํ์ญ์์ค.
- ๊ณผ๋ํ ๋๊ธฐํ (Over-Synchronization): ๋๋ฌด ๋ง์ ๋๊ธฐํ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ์ด ์ ํ๋๊ณ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ ์ ์์ต๋๋ค. ํ์ํ ๋๋ง ๋๊ธฐํ๋ฅผ ์ฌ์ฉํ๊ณ ์๊ณ ์์ญ์ ๊ฐ๋ฅํ ํ ์งง๊ฒ ์ ์งํ์ญ์์ค.
- ํญ์ Lock ํด์ (Always Release Locks): Lock ์ฌ์ฉ์ ๋ง์น ํ์๋ ํญ์ Lock์ ํด์ ํด์ผ ํฉ๋๋ค.
with
๋ฌธ์ ์ฌ์ฉํ์ฌ ์์ธ๊ฐ ๋ฐ์ํ๋๋ผ๋ Lock์ ์๋์ผ๋ก ํ๋ํ๊ณ ํด์ ํ์ญ์์ค. - ์ฒ ์ ํ ํ ์คํธ (Thorough Testing): ๋์์ฑ ๊ด๋ จ ๋ฌธ์ ๋ฅผ ์๋ณํ๊ณ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฉํฐ์ค๋ ๋ ์ฝ๋๋ฅผ ์ฒ ์ ํ ํ ์คํธํ์ญ์์ค. ์ค๋ ๋ ์๋ํ์ด์ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๊ฒ์ฌ๊ธฐ์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ํ์งํ์ญ์์ค.
๊ฒฐ๋ก
Python ์ค๋ ๋ฉ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ๋ง์คํฐํ๋ ๊ฒ์ ๊ฐ๋ ฅํ๊ณ ํจ์จ์ ์ธ ๋์์ฑ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ ๋๋ค. Lock, RLock, Semaphore, Condition Variables์ ๋ชฉ์ ๊ณผ ์ฌ์ฉ๋ฒ์ ์ดํดํจ์ผ๋ก์จ ์ค๋ ๋ ๋๊ธฐํ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ๊ฒฝ์ ์กฐ๊ฑด์ ๋ฐฉ์งํ๋ฉฐ ์ผ๋ฐ์ ์ธ ๋์์ฑ ํจ์ ์ ํผํ ์ ์์ต๋๋ค. ํน์ ์์ ์ ๋ง๋ ์ฌ๋ฐ๋ฅธ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ ํํ๊ณ , ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๋ฉฐ, ์ค๋ ๋ ์์ ์ฑ๊ณผ ์ต์ ์ ์ฑ๋ฅ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ฝ๋๋ฅผ ์ฒ ์ ํ ํ ์คํธํ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค. ๋์์ฑ์ ํ์ ๋ฐ์๋ค์ด๊ณ Python ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฌ๋ ฅ์ ์ต๋ํ ๋ฐํํ์ญ์์ค!