Thread의 safety와 unsafety란?

스레드 안전성(thread-safety)

동시에 여러 스레드가 동일한 데이터에 접근할 때(다중 스레드 환경에서) 데이터의 일관성이 유지되는 것

  • 다중 스레드 환경에서 프로그램의 실행에 문제가 없는 것
  • Thread-sate하다라는 의미는 두 개 이상의 스레드가 race condition에 들어가거나 같은 객체에 동시에 접근해도 연산 결과는 정합성이 보장될 수 있게 메모리 가시성이 확보된 상태다.

  • 다중 스레드 환경에서 적절한 동기화 도구 없이 여러 스레드가 동일한 데이터를 동시에 수정하려 할 경우 데이터의 상태가 불안정(unsafety)해질 수 있다.


스레드 불안전성(thread-unsafety)

동시에 여러 스레드가 데이터에 접근하려 하면 데이터의 일관성이 손상될 수 있는 것

이는 일반적으로 데이터 경쟁 조건(race condition)을 초래하며, 이러한 상태에서는 예측할 수 없는 결과가 발생할 수 있다.

cf)

하나의 프로그램에서 스레드 안전성과 불안전성을 모두 경험할 수 있다. 예를 들어, 특정 부분의 코드는 스레드 안전하게 설계되어 있지만, 다른 부분은 그렇지 않을 수 있다.


경쟁 조건(race condition)

두 개 이상의 프로세스나 스레드가 공유 데이터에 동시에 접근하고, 이들의 실행 순서에 따라 결과가 달라질 때 발생하는 상황을 의미

ex)

  • 전역 변수나 정적 변수를 여러 스레드에서 동시에 변경하려는 경우를 생각해보자. 변수에 대한 쓰기 작업은 일반적으로 원자적(atomic)이지 않다. 즉, 한 스레드가 변수를 읽어 작업을 수행하는 동안 다른 스레드가 동일한 변수에 쓰기 작업을 수행하면 변수의 상태는 예측할 수 없는 상태가 된다. 이러한 상황을 데이터 경쟁 조건이라 한다.

Thread-safety, unsafety와 같은 특성이 생기는 이유

스레드 안전성(Thread-safety)과 불안전성(Thread-unsafety)은 주로 동시성(concurrency)의 존재로 인해 생기는 특성이다. 다시 말해, 여러 개의 스레드가 동시에 작동할 수 있는 환경에서 스레드들이 공유 데이터에 접근하려 할 때 이러한 특성이 생기게 된다.

공유 데이터의 동시 변경

  • 여러 스레드가 공유 데이터를 동시에 수정하려 할 때, 데이터의 일관성이 깨질 수 있다.
    • ex) 한 스레드가 데이터를 읽어서 변경하는 도중 다른 스레드가 동일한 데이터를 수정하는 경우, 첫 번째 스레드가 읽은 데이터는 더 이상 유효하지 않게 된다.

순서 의존성(Order Dependency)

  • 특정 연산이 다른 연산의 결과에 의존하는 경우, 이러한 연산들의 순서는 매우 중요하다.
  • 여러 스레드가 동시에 실행되면, 이러한 연산들의 순서가 보장되지 않을 수 있다.

원자적이지 않은 연산(Non-Atomic Operations)

원자적 연산

  • 중간에 다른 연산이 끼어들 수 없는 연산
  • 원자적이지 않은 연산은 여러 스레드에 의해 동시에 접근되면 데이터의 일관성을 깨트릴 수 있다.

재진입성(Reentrancy)

  • 함수나 메서드가 여러 스레드에 의해 동시에 호출되거나, 재귀적으로 호출될 떄, 공유 데이터의 일관성을 유지하기 위해 재진입성을 보장해야 한다.

동기화 기법

스레드 안전성을 달성(데이터의 일관성을 보장)하기 위해, 공유되는 자원에 대한 접근을 제어하는 여러가지 동기화 기법을 사용한다.

  • 뮤텍스(mutex), 세마포어(semaphore), 모니터(monitor) 등

뮤텍스(mutex)

  • 뮤텍스는 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 하는 도구다.
  • 뮤텍스는 값이 0 또는 1인 세마포어다.
import threading

# 뮤텍스 객체 생성
mutex = threading.Lock()

# 공유 자원에 접근하기 전에 뮤텍스를 획득
mutex.acquire()

# 공유 자원에 대한 작업 수행

# 작업이 끝난 후 뮤텍스를 해제
mutex.release()

세마포어(semaphore)

  • 세마포어는 한 번에 특정 수의 스레드만이 공유 자원에 접근할 수 있도록 제한한다.
import threading

# 세마포어 객체 생성, 동시에 접근할 수 있는 스레드 수를 지정
sem = threading.Semaphore(3)

# 공유 자원에 접근하기 전에 세마포어를 획득
sem.acquire()

# 공유 자원에 대한 작업 수행

# 작업이 끝난 후 세마포어를 해제
sem.release()

모니터(monitor)

  • 모니터는 공유 자원에 대한 접근을 동기화하는 데 사용되는 추상화된 데이터 형식이다.
  • 모니터 내부에는 변수와 메소드가 존재하며, 이들은 한 번에 하나의 스레드만이 접근할 수 있다.
    • 즉, 모니터 내부의 코드는 자동적으로 잠금 메커니즘을 갖고 있다. 이러한 특징으로 인해 모니터는 뮤텍스보다 더 안전한 동기화 메커니즘이다.
import threading

# Condition 객체 생성
cond = threading.Condition()

# 공유 자원에 대한 접근 시작
with cond:
    # 조건이 충족되지 않았다면, wait() 호출
    while not_condition:
        cond.wait()

    # 공유 자원에 대한 작업 수행

# 다른 스레드에게 조건 충족 알림
cond.notify_all()

동기화 기법 특징 정리

동기화 기법설명
뮤텍스한 번에 하나의 스레드만 공유 자원에 접근할 수 있음.
소유한 스레드만 뮤텍스를 해제할 수 있음.
세마포어한 번에 특정 수의 스레드만 공유 자원에 접근할 수 있음.
세마포어는 카운터로 동작하며, 이 카운터의 값이 0이면 블록되고, 다시 증가할 때까지 기다려야 함.
모니터공유 자원에 대한 접근을 동기화하는 데이터 형식.
모니터 내부의 코드는 한 번에 하나의 스레드만이 접근할 수 있으며, 이는 자동적으로 잠금 메커니즘을 갖고 있음.

참고자료

https://developer-ellen.tistory.com/205
https://structuring.tistory.com/151
https://dar0m.tistory.com/234


본 후기는 정보통신산업진흥원(NIPA)에서 주관하는 <AI 서비스 완성! AI+웹개발 취업캠프 - 프론트엔드&백엔드> 과정 학습/프로젝트/과제 기록으로 작성 되었습니다.
#정보통신산업진흥원 #NIPA #AI교육 #프로젝트 #유데미 #IT개발캠프 #개발자부트캠프 #프론트엔드 #백엔드 #AI웹개발취업캠프 #취업캠프 #개발취업캠프

0개의 댓글