0Pricing
Python Academy · 강의

안전한 데이터 공유

큐와 잠금 사용하기

안전한 데이터 공유은(는) CoddyKit의 무료 Python Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Python Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

경쟁 조건 문제

여러 스레드가 같은 데이터를 수정하면 작업 순서가 뒤섞여 결과가 손상될 수 있습니다. 이를 경쟁 조건이라고 합니다. 데이터를 안전하게 공유하려면 동기화 도구가 필요합니다.

import threading

counter = 0

def bump():
    global counter
    for _ in range(100000):
        counter += 1

ts = [threading.Thread(target=bump) for _ in range(2)]
for t in ts: t.start()
for t in ts: t.join()
print('counter (may be < 200000):', counter)

Lock으로 해결하기

threading.Lock은 한 번에 하나의 스레드만 임계 영역에 들어가도록 보장합니다. 공유 데이터를 수정하는 부분을 with 블록으로 감싸 잠금을 획득하세요.

import threading

counter = 0
lock = threading.Lock()

def bump():
    global counter
    for _ in range(100000):
        with lock:
            counter += 1

ts = [threading.Thread(target=bump) for _ in range(2)]
for t in ts: t.start()
for t in ts: t.join()
print('counter:', counter)

acquire와 release

잠금은 acquire()와 release()를 사용하여 수동으로 다룰 수도 있지만, with 형식이 더 안전합니다. 오류가 발생해도 잠금을 해제하기 때문입니다.

import threading

lock = threading.Lock()
lock.acquire()
try:
    print('inside critical section')
finally:
    lock.release()
print('released')

스레드 안전 큐

queue.Queue 클래스는 이미 스레드 안전성을 갖추고 있습니다. 스레드는 명시적인 잠금 없이 put과 get을 사용할 수 있으므로, 데이터를 전달할 때 선호되는 방법입니다.

import queue, threading

q = queue.Queue()

def producer():
    for i in range(3):
        q.put(i)

t = threading.Thread(target=producer)
t.start()
t.join()
while not q.empty():
    print('got', q.get())

생산자와 소비자

고전적인 패턴은 다음과 같습니다. 생산자는 작업을 큐에 put하고, 소비자는 작업을 get하여 처리합니다. 큐가 모든 잠금 처리를 담당합니다.

import queue, threading

q = queue.Queue()
results = []

def consumer():
    while True:
        item = q.get()
        if item is None:
            break
        results.append(item * 10)
        q.task_done()

t = threading.Thread(target=consumer)
t.start()
for i in range(3):
    q.put(i)
q.put(None)
t.join()
print(results)

join과 task_done

q.join()은 put된 모든 항목에 대응하는 task_done() 호출이 이루어질 때까지 실행을 멈춥니다. 이를 통해 주 스레드가 모든 작업이 끝날 때까지 기다릴 수 있습니다.

import queue, threading

q = queue.Queue()

def worker():
    while True:
        item = q.get()
        q.task_done()

threading.Thread(target=worker, daemon=True).start()
for i in range(5):
    q.put(i)
q.join()
print('all items processed')

재진입을 위한 RLock

일반적인 Lock은 같은 스레드가 두 번 획득하려 하면 교착 상태에 빠집니다. threading.RLock(재진입 잠금)은 획득 횟수를 세면서 이를 허용합니다.

import threading

rlock = threading.RLock()

def outer():
    with rlock:
        inner()

def inner():
    with rlock:
        print('re-entered safely')

outer()

신호 전달을 위한 Event

threading.Event를 사용하면 한 스레드가 다른 스레드에 신호를 보낼 수 있습니다. 스레드는 다른 스레드가 set()을 호출할 때까지 wait()를 호출합니다.

import threading

start = threading.Event()

def worker():
    start.wait()
    print('go!')

t = threading.Thread(target=worker)
t.start()
print('setting event')
start.set()
t.join()

프로세스 간 공유

프로세스 간에는 대신 multiprocessing.Queue를 사용하세요. 이 큐는 서로 분리된 메모리 공간 사이에서 객체를 직렬화합니다.

import multiprocessing as mp

def producer(q):
    for i in range(3):
        q.put(i * i)

if __name__ == '__main__':
    q = mp.Queue()
    p = mp.Process(target=producer, args=(q,))
    p.start()
    p.join()
    while not q.empty():
        print('received', q.get())

교착 상태 피하기

스레드가 서로의 잠금이 해제되기를 영원히 기다릴 때 교착 상태가 발생합니다. 여러 잠금을 획득해야 한다면 어디서나 항상 같은 순서로 획득하여 이를 피하세요.

import threading

lock_a = threading.Lock()
lock_b = threading.Lock()

def safe():
    with lock_a:
        with lock_b:
            print('acquired in consistent order')

safe()

잠금보다 큐 선호하기

가능하면 잠금으로 보호하는 변경 가능한 공유 상태보다 큐를 이용한 메시지 전달을 선호하세요. 이렇게 하면 동작을 더 쉽게 추론할 수 있고 버그가 생길 가능성도 훨씬 낮습니다.

import queue

q = queue.Queue()
for word in ['safe', 'simple', 'clear']:
    q.put(word)
while not q.empty():
    print(q.get())

빠른 확인

데이터를 안전하게 공유하는 방법에 대한 이해도를 확인해 보세요.

복습

데이터를 안전하게 공유하는 방법을 배웠습니다:

  • 보호되지 않은 공유 상태는 경쟁 조건으로 인해 손상됩니다.
  • Lock과 RLock은 임계 영역을 보호합니다.
  • queue.Queue는 스레드 안전하며, 프로세스 간에는 multiprocessing.Queue를 사용하세요.
  • Event는 스레드 간에 신호를 보내며, 일관된 잠금 순서를 사용하면 교착 상태를 피할 수 있습니다.

다음 과정: subprocess로 외부 프로그램 실행하기입니다.

자주 묻는 질문

“안전한 데이터 공유” 강의는 무료인가요?

네 — “안전한 데이터 공유” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Python Academy 강의 전체를 잠금 해제할 수 있습니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“안전한 데이터 공유”에서 뭘 배우나요?

큐와 잠금 사용하기 브라우저에서 직접 실행하는 실습 코드로 Python Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Python Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Python Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“안전한 데이터 공유” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Python Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Python Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 스레드와 GIL
  2. ThreadPoolExecutor
  3. multiprocessing 기초
  4. 안전한 데이터 공유
← Python Academy(으)로 돌아가기