[thread] Double-Checked Locking

[thread] Double-Checked Locking

태그
python
생성 일시
May 7, 2024 07:11 AM
최종 편집 일시
Last updated May 7, 2024
Date
본 게시글은 python 코드를 기준으로 작성 습니다.

서론

Double-Checked Locking (DCL)은 소프트웨어 설계에서 멀티스레드 프로그래밍을 위해 사용되는 패턴이다. 싱글톤 또는 비슷한 자원을 효율적이고 안전하게 초기화하는데 사용된다.

DCL(Double-Checked Locking) 이란?

# DCL(Double-Checked Locking)이 적용된 싱글톤 파이썬 클래스 import sqlite3 import threading class Database: _instance = None _lock = threading.Lock() def __new__(cls): if cls._instance is None: # 첫 번째 검사: 인스턴스가 이미 생성되었는지 확인 (1) with cls._lock: # 락 획득 (2) if cls._instance is None: # 두 번째 검사: 다시 인스턴스 생성 여부 확인 (3) cls._instance = super(Database, cls).__new__(cls) # 데이터베이스 연결 초기화 (4) cls._instance.connection = sqlite3.connect('db.sqlite3', check_same_thread=False) cls._instance.cursor = cls._instance.connection.cursor() return cls._instance

Double-Checked Locking의 기본 원리

  1. 첫 번째 검사: 객체가 이미 초기화되었는지 락(lock)을 획득하지 않고 빠르게 확인한다. 이미 초기화된 경우, 바로 리턴하여 자원을 아낀다. (1)
  1. 락 획득: 객체가 아직 초기화되지 않았다면, 락을 획득한다. 이 단계에서 추가 비용이 발생하지만, 필요할 때만 발생한다. (2)
  1. 두 번째 검사: 락을 획득한 후, 다시 한번 객체가 초기화되었는지 확인한다. 이는 다른 스레드가 락을 대기하는 동안 객체를 초기화했을 수 있기 때문이다. (3)
  1. 객체 초기화: 두 번째 검사에서도 객체가 초기화되지 않았다면, 객체를 초기화한다. (4)
  1. 락 반환: 초기화 과정을 마치고 락을 반환한다. (2)

사용 시 고려해야 할 문제점

  • 메모리 모델 문제: C++ 같은 언어에서는 메모리 모델의 일관성이 보장되지 않아, 초기화가 완전히 완료되기 전에 객체의 참조가 다른 스레드에 의해 볼 수 있어 문제를 일으킬 수 있다. → 이는 가시성(visibility) 문제로 알려져 있음.
  • 컴파일러 최적화: 일부 언어나 컴파일러에서는 DCL의 두 검사 사이의 연산 순서가 최적화 과정에서 변경되 코드가 의도와 다르게 동작할 수 있다. 하지만 파이썬은 인터프리터 언어이기에 이것이 문제가 되지 않는다.

Python에서의 DCL

Python에서는 Global Interpreter Lock (GIL) 때문에 일반적으로 메모리 가시성 문제에 대해 걱정할 필요가 없다(모든 가시성 문제를 해결하지는 않는다). GIL은 한 시점에 단 하나의 스레드만이 Python 코드를 실행할 수 있게 함으로써, 데이터의 일관성과 안전성을 보장하기 때문이다.

결론

DCL은 멀티스레드 환경에서 자원의 비용이 큰 초기화 작업을 효율적으로 처리하려는 방법이다. 그러나 이를 적용할 때는 해당 언어의 메모리 모델, 컴파일러의 최적화 동작을 잘 이해하고 있어야 한다. Python에서는 GIL 때문에 DCL의 전통적인 문제점이 크게 부각되지 않는다.
참고 문헌