본문 바로가기
C.W.K.
Stream
Lesson 10 of 10 · published

Thread Safety + check_same_thread

~12 min · python, threading, thread-safety

Level 0Scout
0 XP0/80 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

곧장 부딪히게 될 flag

Python sqlite3.Connection은 기본적으로 자기를 만든 thread에서만 쓸 수 있어. 다른 thread에서 쓰면 ProgrammingError: SQLite objects created in a thread can only be used in that same thread가 튀어나와.

여러 thread를 쓰는 앱에서 이걸 푸는 방법은 셋이야.

  1. thread마다 connection 하나 — 보통 threading.local()로 해. 단순하고 안전해.
  2. Connection pool — 수명이 짧은 thread가 많은 앱에 맞아. SQLAlchemy 같은 도구가 알아서 챙겨줘.
  3. check_same_thread=False에 직접 만든 lock — 안전장치를 끄고 접근을 네가 줄 세우는 거야. 망치기 쉬우니까 지금 뭘 하는지 정확히 알 때만 해.
Self-reference: 피파 backend는 thread 대신 aiosqlite를 써. track 7에서 다뤄. 그러면 이 문제 자체를 통째로 비켜가. async task는 정의상 한 thread 안에 있으니까. sync로 thread를 쓰는 코드라면 thread마다 connection을 두는 게 심심하지만 정확한 기본이야.

Code

Per-thread connection 패턴·python
import sqlite3, threading

_local = threading.local()

def conn() -> sqlite3.Connection:
    if not hasattr(_local, 'c'):
        _local.c = sqlite3.connect('demo.db', timeout=30.0)
        _local.c.execute('PRAGMA journal_mode = WAL')
        _local.c.execute('PRAGMA foreign_keys = ON')
    return _local.c

def worker():
    rows = conn().execute('SELECT count(*) FROM users').fetchone()
    print(threading.current_thread().name, rows)

threads = [threading.Thread(target=worker) for _ in range(8)]
for t in threads: t.start()
for t in threads: t.join()

External links

Exercise

thread를 열여섯 개 띄우는 작은 프로그램을 써봐. 각 thread가 SQLite DB에 읽기와 쓰기를 섞어서 던지는 거야. thread마다 connection을 두는 패턴으로 구현하고, WAL 덕분에 reader와 writer가 서로 안 막는지 확인해. 그다음 일부러 connection 하나를 여러 thread가 나눠 쓰게 만들어서 ProgrammingError를 직접 봐.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.