2020-10-08 TIL

4 minute read

데이터 레이스

  • 스레드를 시작시키는 순간 running 상태로 접어든다.
    • running 상태는 실행하고 있는 상태 뿐만 아니라, CPU를 받을 수 있는 상태이기도 하다.
    • CPU는 OS의 관리 정책에 따라 스레드나 프로세스에 배분된다. 물론 OS가 CPU를 배분한 후 임의시간 후에 다시 회수하여 다른 스레드(현재 스레드 포함)나 프로세스에 배분한다. 때에 따라서 같은 스레드가 연속해서 배분 받는 경우도 있을 것이다.
  • 멀티 쓰레드 환경에서 여러 쓰레드가 공유자원에 동시에 접근할 때 쓰레드의 경쟁에 유발되는 문제
  • 데이터 레이스를 없애는 가장 직접적인 방법은 Lock을 사용하는 것
    • 속도 저하 문제가 발생
  • Lock을 덜 사용하도록 프로그램을 변경
    • 어렵다
    • 완전히 Lock을 없앨 수 없다
  • 데이터레이스를 없애기 위해 데이터레이스 앞뒤로 락을 두면 엄청난 속도 저하가 있음
  • 락을 덜 사용하도록 데이터레이스가 적게 프로그램을 변경하는 것은 어렵고 데이터레이스를 완전히 없앨 수 없다.
    • 데이터레이스를 없앤다는 것은 스레드끼리 서로 공유 메모리를 통해 데이터를 주고받지 않겠다는 것인데 이것은 줄일수는 없애도 완전히 없앨수 없다. 완전히 없앤다는 것은 스레드끼리 데이터를 주고 받는 일이 없도록 만들어 협업하지 않도록 프로그래밍을 한다는 이야기이다.
  • 락을 회피하는 프로그래밍
    • 데이터레이스를 줄여서 락의 필요성을 줄인다.
    • 데이터레이스가 있지만 전후 사정을 잘 파악해서 락을 넣지 않아도 잘 수행되도록 프로그래밍 한다.

동기화

  • 데이터 레이스를 방지하기 위해 동기화 기법을 사용
  • 스레드끼리 데이터를 주고받거나 실행 순서를 맞추는 행위
  • 협업을 위해 필수

컨텍스트 스위칭(Context Switching)

  • https://agh2o.tistory.com/12
  • 프로세스나 스레드를 실행하기 위해 실행 정보를 읽어오고 저장하는 것
  • 멀티프로세스 환경에서 CPU가 어떤 하나의 프로세스를 실행하고 있는 상태에서 인터럽트 요청에 의해 다음 우선 순위의 프로세스가 실행되어야 할 때 기존의 프로세스의 상태 또는 레지스터 값(Context)을 저장하고 CPU가 다음 프로세스를 수행하도록 새로운 프로세스의 상태 또는 레지스터 값(Context)를 교체하는 작업을 Context Switch(Context Switching)라고 한다.
  • 동시에 여러 개의 프로세스나 스레드를 실행할 때 CPU 사용권을 뺏어 다른 프로세스나 스레드에게 주기 전에 현재까지 실행한 코드의 위치 정보를 저장해야 한다. 또한 CPU 사용권을 주기 전에 그 프로세스나 스레드가 이전에 어디까지 실행했었는지 이전 실행 위치 정보를 로딩해야 한다. 즉 실행 위치에 대한 정보를 저장하고 로딩하는 것을 말한다.

프로세스(스레드) 스케줄링

  • OS가 프로세스나 스레드에 CPU 사용을 배분하는 정책
    • 1) Round-Robin 방식
      • Windows 운영체제에서 사용하는 방식이다.
      • 우선 순위 보다는 일정 시간 위주로 프로세스나 스레드에게 CPU를 배분하는 방식이다.
    • 2) Priority + Aging 방식
      • Unix나 Linux 운영체제에서 사용하는 방식이다.
      • 우선 순위가 높은 프로세스나 스레드에게 CPU를 먼저 배분하는 방식이다.
      • 우선 순위 배분 방식에서는 우선 순위가 낮은 경우 실행에서 소외되는 문제가 발생하기 때문에
  • 우선 순위가 높은 프로세스나 스레드 때문에 실행 순서가 밀릴 때 마다 원래의 낮은 순위를 높임으로써(aging) 결국에는 모든 프로세스와 스레드의 실행을 완료할 수 있게 한다.

우선순위

  • 우선 순위가 높으면 CPU 사용 배분을 좀 더 자주 받는다. => 스레드는 JVM에서 관리하는 것이 아니라 OS가 관리한다. => 즉 OS의 스레드를 이용하는 것이다. => 따라서 우선 순위에 따라 실행 스케줄을 어떻게 관리할지는 OS에 따라 다르다. => Windows OS는 우선 순위를 크게 고려하지 않는다. 그래서 Windows에서 실행할 때는 우선 순위에 영향을 적게 받을 것이다. => Unix, Linux 계열 OS는 우선 순위를 고려한다. 그래서 이런 OS에서 실행할 때는 우선 순위에 영향을 받을 것이다.

    주의! => Java 의 캐치프레이즈가 “Write Once, Run Anywhere!” 이다. => 즉 OS에 상관없이 동일하게 동작하게 만드는 것이 자바의 목적이다. => 그런데 우선 순위에 따라 실행률이 달라지고, OS 마다 차이가 난다면, 자바의 목적에 부합하는 것이 아니다. => 그래서 가능한 OS에 영향을 덜 받는 방식으로 코딩해야 한다. => 이런 이유로 스레드를 다룰 때 우선 순위를 고려하는 방식으로 프로그래밍을 하지 말라!

비동기 방식으로 인한 문제

  • 여러 개의 스레드가 같은 객체에 대해 메서드를 호출하여 동시에 값을 변경하려 할 때 서로 그 메모리의 값을 덮어쓰는 문제가 발생한다. 이처럼 여러 스레드가 동시에 실행할 때 문제를 일으키는 코드를 “임계 구역(Critical Section; Critical Region)”이라 부른다. 이 예제에서는 여러 스레드가 동시에 호출하고, 같은 인스턴스의 변수 값을 변경하는 메서드인 “withdraw()”가 critical section이다.

    해결책? => 한 번에 한 스레드 만이 크리티컬 섹션을 실행하도록 접근을 제한하면 된다. 주의! => 동시에 여러 스레드가 같은 메모리에 대해 값을 조회할 때는 문제가 발생하지 않는다.

    세마포어(n); semaphore => 크리티컬 섹션에 진입할 수 있는 스레드의 수를 지정한다. => 자바에서는 세마포어를 지원하지 않는다. => 개발자가 직접 처리해야 한다.

    뮤텍스; mutex(mutual exclusion, 상호배제) => 한 번에 오직 한 개의 스레드만이 크리티컬 섹션에 접근할 수 있다. => 예) 선풍기 풍량세기, 라디오 채널, TV 채널 등 => semaphore(1)과 같다. => 자바는 synchronized 키워드를 통해 뮤텍스를 사용할 수 있다.

  • 용어정리! 임계 구역(critical section)
  • 여러 스레드가 동시에 실행할 때 문제가 발생하는 코드 블록을 말한다.
  • critical region 이라고도 부른다.
  • 같은 메모리에 여러 스레드가 동시에 접근하여 값을 변경하려 할 때 문제가 발생하는 것이다. 즉 다른 스레드가 사용하는 변수의 값을 임의로 변경하면 원래의 의도대로 동작하지 않게 되는 것이다.
  • “스레드 안전(thread safe)하지 않다”라고 말한다.

스레드 안전(thread safe)

  • 여러 스레드가 동시에 실행하더라도 아무런 문제가 되지 않는 코드를 말한다.
  • 여러 스레드가 같은 메모리에 접근하더라도 읽기만 한다면 아무런 문제가 되지 않는다.

세마포어(semaphore)

  • critical section에 접근하는 스레드의 수를 제어하는 기법
  • 보통 다음의 형식으로 표시한다. semaphore(n) n은 개수를 의미한다. 예) semaphore(3) : 동시에 3개의 스레드가 접근할 수 있다는 의미다.
  • 자바는 2개 이상의 접근을 허용하지 않는다. 오직 한 개만 가능하다.

뮤텍스(mutual exclusion; MUTEX)

  • critical section에 오직 한 개의 스레드만이 접근하는 것.
  • semaphore(1) 과 같다.
  • 자바에서 synchronized 는 해당 블록(critical section)을 뮤텍스로 선언하는 것이다.

뮤텍스

  • 데드락을 피해야 한다.
  • 어느 하나의 스레드가 메모리에 들어가서 특정 조건이 만족되면 락을 해제하고 나오려고 하는데, 다른 스레드가 특정 조건이 만족되면 들어가려고 하는 상황.
  • 예) 화장실 변기칸에서 안에 있는 사람은 이름을 적으면 문을 열고 나온다고 하고 밖에 있는 사람은 문을 열어주면 이름을 적는다고 하는 상황.
  • 실행 멈춤.
  • 특정 개수의 스레드만이 진입할 수 있도록 통제하는 것을 세마포어라고 한다.
    • 자바 기본 문법이 없다.
    • 직접 구현해야 한다.

sleep()

  • 해당 시간동안 cpu 사용권을 받지 않겠다는 의미.

Categories:

Updated: