본문 바로가기
IT KNOWLEDGE

경쟁조건(Race Condition)

by BTC_동동 2023. 7. 21.

안녕하세요 여러분 베하!!

또 다시 비가 많이 내릴 예정이라 하니 걱정입니다. 다들 몸 부터 먼저 챙기셔야 합니다.

오늘은 경쟁조건에 대해 공부하려고 합니다.

공유메모리

메모리 공유

위 그림에서 특정 프로세스는 여러 스레드를 가지고 있고 프로세스는 메모리 구조상 코드, 데이터, 힙, 스택영역을 차지합니다. 이때 프로세스 내부의 스레드는 스택메모리를 제외한 나머지 메모리는 모두 공유하여 사용하고 있습니다.

프로세스는 부서, 스레드는 부서의 사람이고 스택은 책상이며 코드, 데이터, 힙은 부서의 업무에 필요한 데이터가 있는 서랍이라고 생각하겠습니다.

우리는 출근하면 부서의 데이터를 가져와서 자신의 책상에 올리고 자신이 그 업무를 처리한다생각하면 한 스레드가 스택영역을 제외한 나머지는 공유한다고 볼 수 있습니다.

 

경쟁조건(Race Condition)

특정 부서의 두 명의 사람이 서랍에 있는 매출 데이터에 1,000원을 더한다고 생각해보겠습니다. 매출 데이터에는 10,000원이 입력되어 있고 두 명의 사람이 매출 데이터에 1,000원을 더했기 때문에 12,000이라는 데이터를 예측할 수 있습니다.

하지만 사람 A가 먼저 10,000원 이라는 데이터를 읽어와서 11,000원 데이터를 기록하고 있을 때 사람 B도 데이터를 읽어와서 11,000데이터를 기록하고 사람 A가 서랍에 데이터가 기록된 파일을 교체한다고 할 때 사람 B도 자신의 데이터가 기록된 파일을 교체하기 때문에 최종적으로 서랍에는 12,000이 아닌 11,000이 기록되게 됩니다.

 

이런것을 2개 이상의 프로세스나 스레드가 공유 자원을 병행적으로 읽거나 쓰는 상황을 말하고 공유된 자원에 접근 순서인 경쟁에 따라 기대하는 결과가 달라지는 상황을 말합니다.

 

경쟁조건 코드

public class Main {
    
	static class Competition {
        int count =0;
        public Competition() throws InterruptedException {

            Thread th1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    count();
                }
            });
            Thread th2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    count();
                }
            });
            th1.start();
            th2.start();
            Thread.sleep(10);
            System.out.println(count);
        }
        void count() {
            for (int i =0; i <100000; i ++){
                count ++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("일단고");
        new Competition();
    }

위 코드를 보면 Competition 클래스는 두개의 스레드를 통해서 작업을 진행한다. 작업은 count()함수가 됩니다. 예상대로라면 100000을 두번 더하면 200000의 결과값을 기대하지만 실행시켜보면 다른 것을 알 수 있습니다.

 

1. 첫 실행

    일단고

    126275

2. 두번째 실행

    일단고

    145945

3. 세번째 실행

    일단고

    153702

 

위 결과값을 보면th1이 이미 올려놓은 값에 th2가 이전 값을 할당함을 추측할 수 있습니다. 이런 것을 자원의 경쟁조건입니다.

th1이라는 친구와 th2라는 친구가 버튼을 누른다고 할 때 두 친구 모두 10000번을 눌러 20000의 숫자를 채우려고 했지만 th1과 th2가 동시에 버튼을 누른 부분은 count에 2값을 올린게 아닌 1만 올린게 된 것이라 볼 수 있습니다.

 

특히나 이 경쟁조건의 발생으로 인한 문제를 디버깅하기에는 추적이 상당히 어렵습니다. 결과값을 보면 실행마다 늘 값이 다르다는 것을 알 수 있습니다.

기댓값 20000에 미치지 못한 값을 차이를 계산하면 그 값만큼 데이터 동시접근이 발생했다고 볼 수 있습니다.

앗 그리고 count 값을 너무 작게하면 경쟁조건을 눈으로 확인하기 어렵습니다. 컴퓨터는 사람이 생각하는 것보다 아주 빨리 계산을 진행합니다. 어느정도의 병목을 줄 필요가 있습니다.

 

경쟁조건 예방

이러한 스레드들의 메모리 공유로 인한 문제 예방을 위해서는 하나의 한 스레드만 메모리에 접근할 수 있게 제한해줘야 합니다. 이를 위해 임계 구역을 정의하는데 임계구역은 공유된 자원들의 위치이고 프로세스나 스레드가 이 임계구역을 진입할 때 한 프로세스나 스레드만 진입할 수 있게 제한해야 한다는 말입니다.

 

이 제한을 위해서는 3가지 조건을 충족시켜야 합니다.

  1. 상호 배제
    • 한 번에 두개의 스레드가 접근X
  2. 진행
    • 임계구역에 다른 스레드가 접근하지 않는 상황이라면 항상 접근이 가능하게 해야합니다.
  3. 한정대기
    • 자원을 요청한 스레드가 계속 기다리지 않게 해야 합니다.

해당 조건을 만족한다면 위 이미지의 실행순서는

한 스레드가 임계구역(공유영역)에 접근할 때 두 스레드는 쉰다. 접근 후 작업이 끝나면 해당 구역에서 벗어나게 하고 다른 스레드의 접근을 허용하고 똑같이 과정을 반복합니다.

 

오늘은 이렇게 경쟁조건에 대해 간단히 알아보고 코드도 구현해봤습니다. 

다음에 다시 만나요

'IT KNOWLEDGE' 카테고리의 다른 글

백엔드란?  (0) 2023.07.21
JWT 토큰과 세션의 차이점  (0) 2023.07.21
Rancher로 Kubernetes 환경 구축하기 - 클러스터 구축  (0) 2023.07.21
HTTP Request, Response 구조  (0) 2023.07.17
캐시와 레지스터  (0) 2023.07.11

댓글