동기화, 경쟁 조건, 임계 영역

Minseok Kim·2023년 1월 4일
0

해당 포스트는 쉬운코드님의 유튜브 영상을 정리한 내용입니다.


같은 프로세스에 속하는 쓰레드들은 힙 영역을 공유하기 때문에, 힙 영역의 데이터를 여러 쓰레드들이 동시에 접근하면 데이터 값이 예상과 다르게 바뀔 수 있다.

이를 막기 위해 임계 영역을 만들어 상호 배제한다.

경쟁 조건은 서로 다른 프로세스 간에도 발생할 수 있다.

서로 다른 프로세스의 경우에는 기본적으로 메모리 영역을 공유하지 않지만, 공유 메모리 영역을 만들고 여러 프로세스를 해당 메모리 영역에 매칭시키면, 쓰레드들처럼 프로세스들도 같은 메모리 공간에 접근 가능하기 때문에 경쟁 조건이 발생할 수 있다.

하나의 객체를 두 개의 쓰레드가 접근할 때 생기는 일

state++; 가 CPU에서 어떻게 실행되는지를 아는 것이 중요하다!

T1에서 state++ 수행 중에 STORE R1 to STATE 연산을 하기 전에 컨텍스트 스위칭이 일어난다면, 아직 state는 변하지 않았기 때문에 증가 연산이 중복되어 씹힌다.

하나의 쓰레드에서 언제 컨텍스트 스위칭이 일어나느냐에 따라서 결과가 달라진다.

예제는 싱글 코어지만 듀얼 코어 등에서도 충분히 일어날 수 있는 일이다.

이를 race condition이라 한다.

race condition

여러 프로세스 / 쓰레드가 동시에 같은 데이터를 조작할 때 타이밍이나 접근 순서에 따라 결과가 달라질 수 있는 상황

동기화

여러 프로세스 / 쓰레드를 동시에 실행해도 공유 데이터의 일관성을 유지하는 것

어떻게 동기화 시킬 것인가?

  • state++; 같은 복합 명령문을 atomic 처리해 컨텍스트 스위칭이 일어나지 않게 하기
    • 이 방법은 싱글 코어에서만 가능하고 멀티 코어에서는 불가능하다.
    • 멀티 코어 상황일 때는 한 코어에서 스위칭을 막아도 다른 코어에서 접근하는 것은 막지 못하기 때문
  • counter.increment() 메서드를 한번에 한 쓰레드만 실행할 수 있도록 하기
    • 한번에 메서드를 한 쓰레드만 실행, 다른 쓰레드는 대기
      • 멀티 코어여도 문제될 것이 없다.
    • 임계 영역
      • 공유 데이터의 일관성을 보장하기 위해 하나의 프로세스 / 쓰레드만 진입해서 실행 가능한 영역

critical section problem의 해결책이 되기 위한 조건

  1. mutual exclusion - 상호 배제
  2. progress - 진행 (임계 영역이 비어있을 때 대기하는 것중 하나를 들여보낸다)
  3. bounded waiting - 한정된 대기 (임계 영역에 들어가기까지 무한정 대기하면 안됨)

3가지 조건을 모두 만족해야 critical section 문제의 해결책이 될 수 있다.

자바에서 기본적으로 제공하는 클래스도 모두 Thread safe한 것은 아니다. 문서를 잘 읽어야 한다.


파이썬에는 cachetools라는 라이브러리로 로컬 캐싱을 사용한다.

이 cachetools에서 제공하는 캐시가 쓰레드 세이프하게 동작하기 위해서는 캐시 선언하면서 파라미터에 Lock을 넣어줘야 하는데, 문서나 예제 코드들에서는 굳이 락을 넣어주지 않아 실수할 여지가 존재한다. 디폴트가 쓰레드 세이프하지 않기 때문

이로 인해 캐싱 데이터가 만료되거나 삭제될 때 에러가 발생할 수 있다.

  • 특정 쓰레드가 이미 지운걸 다른 쓰레드가 다시 지우는 현상
  • 캐시 사이즈는 선언될 때 정해지고 캐싱된 아이템을 삭제할 때 원래 사이즈대로 늘려줘야 한다. 하지만 동시성 에러가 발생하면서 캐시 사이즈가 점점 줄어들 수 있다.
    • 쓰레드 수는 그대로인데 캐시사이즈는 점점 줄어들게 되어 더 많은 에러가 발생!

0개의 댓글