맨위로가기

임계 구역

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

임계 구역은 여러 프로세스나 스레드가 공유 자원에 동시에 접근할 때 발생하는 데이터 손상 문제를 해결하기 위한 프로그래밍 개념이다. 임계 구역으로 지정된 코드 영역은 한 번에 하나의 프로세스 또는 스레드만 접근하도록 제한하여 데이터의 일관성을 보장한다. 웹 페이지 방문자 수 카운터, 입출금 처리, 생산자-소비자 문제 등 공유 자원을 사용하는 다양한 상황에서 활용되며, 상호 배제를 위해 세마포어, 뮤텍스, 잠금 등의 동기화 메커니즘을 사용한다. 운영체제 및 프로그래밍 언어는 이러한 임계 구역을 구현하기 위한 API를 제공한다.

더 읽어볼만한 페이지

  • 운영체제 기술 - 프로세스
    프로세스는 컴퓨터에서 실행되는 프로그램의 인스턴스로, 운영 체제가 시스템 자원을 효율적으로 관리하며 멀티태스킹 환경에서 독립적인 실행 흐름을 유지한다.
  • 운영체제 기술 - 커널 (컴퓨팅)
    커널은 운영 체제의 핵심으로, 하드웨어와 소프트웨어 간 상호 작용을 관리하며 시스템 보안, 자원 관리, 하드웨어 추상화, 프로세스 스케줄링, 프로세스 간 통신, 다중 작업 환경 지원 등의 기능을 제공하고, 모놀리식, 마이크로, 혼합형 커널 등으로 구현되며 가상화 및 클라우드 컴퓨팅 환경에서 중요성이 커지고 있다.
임계 구역

2. 임계 구역의 필요성

서로 다른 코드 또는 프로세스가 동일한 변수를 읽거나 쓸 때, 작업 순서에 따라 결과가 달라질 수 있다. 예를 들어, 프로세스 A가 변수 x를 읽고, 동시에 프로세스 B가 x에 값을 쓰면, 프로세스 A는 x의 이전 값이나 새 값을 얻게 되어 의도하지 않은 결과가 나올 수 있다.

임계 구역의 필요성을 나타내는 흐름 그래프


아래는 이러한 상황을 보여주는 예시이다.

'''프로세스 A:'''

```

b = x + 5; // 명령 실행 시간 = Tx

```

'''프로세스 B:'''

```

x = 3 + z; // 명령 실행 시간 = Tx

```

프로세스 A와 B를 동시에 실행하면 원하는 결과를 얻지 못할 수 있다. 이를 방지하기 위해 변수 x는 임계 구역으로 보호한다. 먼저 B가 임계 구역에 접근하여 값을 쓰고, B가 작업을 마치면 A가 임계 구역에 접근하여 변수 x를 읽는 방식으로 동시 접근 문제를 해결한다.

이처럼 임계 구역은 공유 변수에 대한 동시 접근을 방지하고, 다중 스레드 프로그램에서 데이터 충돌 없이 여러 변수를 업데이트하거나, 프린터와 같은 공유 자원에 한 번에 하나의 프로세스만 접근하도록 보장하는 데 사용된다.

2. 1. 예시: 웹 페이지 방문자 수 카운터

웹 페이지 방문자 수를 나타내는 카운터 프로그램을 예로 들어 설명한다. 카운터 프로그램은 대략적으로 다음 처리를 수행한다.[1]

# 디스크 등의 기억 장치 상의 파일에서 현재 카운터 값을 메모리로 읽어온다.

# 메모리 상에서 카운터 값을 1 증가시킨다.

# 카운터 값을 메모리에서 기억 장치로 다시 기록한다.

실제로는 이러한 처리 각각이 여러 개의 세부적인 명령 집합으로 구성되는 것이 일반적이다.[1]

프로그램의 프로세스는 항상 하나만 실행되지만, 개별적인 액세스는 태스크화되어 이를 처리하는 여러 스레드로 분배되는 것으로 한다.[1]

여기서 현재 디스크에 기록된 카운터 값이 '''100'''이었다고 가정한다.[1]

사용자 A가 이 웹 페이지를 방문하여 카운터 프로그램의 '''스레드 A'''가 실행되기 시작했다고 하자. 스레드 A는 '''처리 1'''에서 카운터 값을 읽어 들이고(값은 '''100'''), '''처리 2'''에서 값을 증가시킨다(값은 '''101'''). 다음으로 '''처리 3'''에서 값을 다시 기록하는데, 여기서 사용자 B가 웹 페이지를 방문하여 다른 '''스레드 B'''가 실행되고, 즉시 컨텍스트 스위칭이 발생하여 처리가 스레드 B로 전환되었다고 가정한다. 스레드 B가 카운터 값을 읽으면 값은 '''100'''이 된다. 왜냐하면 스레드 A가 아직 '''처리 3'''을 완료하지 않아 디스크 상의 값은 변화하지 않았기 때문이다. 그리고 스레드 B는 값을 증가시키고, 그 결과인 '''101'''이라는 값을 디스크에 기록하고 스레드를 종료한다. 다음에 다시 스레드 A로 처리가 전환되어 스레드 A는 '''처리 3'''을 수행하고, 디스크에는 '''101'''이라는 값이 기록되어 스레드 A도 처리를 종료한다.[1]

결과적으로 사용자 A와 사용자 B 두 명이 페이지를 방문했으므로, 원래 카운터 값은 2 증가하여 '''102'''가 되어야 하지만, 최종적으로 디스크에 기록된 값은 '''101'''이 되어 문제가 발생한다.[1]

이상의 처리를 시간에 따라 정리한 것이 다음 표이다.[1]

디스크 상의 값스레드 A(값)스레드 B(값)
100스레드 발생
100처리 1(100)
100처리 2(101)
100대기스레드 발생
100처리 1(100)
100처리 2(101)
101처리 3(101)
101스레드 종료
101처리 3(101)
101스레드 종료



배타적 제어를 사용한 임계 구역은 '''하나의 스레드만이 사용 권한을 얻을 수 있는 프로그램상의 처리 영역'''이다. 이 사용 권한은 락(lock)이라고 불리기도 한다.[1]

어떤 스레드가 배타적 제어를 한 임계 구역에 들어가 있는 동안, 다른 스레드는 임계 구역에 들어갈 수 없다. 보통 그 스레드는 대기 상태가 된다.[1]

이 카운터 프로그램의 경우, 프로그램 시작 부분, 즉 위의 경우에서 처리 1 전에 '''배타적 제어의 락을 획득하여 임계 구역에 들어가는''' 처리를 추가할 필요가 있다. 그리고 스레드가 종료되기 전에 '''배타적 제어의 락을 해제하여 임계 구역에서 나오는''' 처리를 추가하면 완료된다.[1]

여기서, 앞서와 마찬가지로 '''스레드 A'''가 처리 1, 처리 2를 끝내고 처리 3을 실행하기 전에, '''스레드 B'''가 발생했다고 가정한다. 그러나, 여기서 이미 스레드 A가 락을 획득하여 임계 구역에 들어가 있기 때문에, 스레드 B는 락을 획득할 수 없어 처리를 시작할 수 없으므로 대기 상태가 된다. 그리고 스레드 A가 처리를 마치고, 임계 구역에서 나오면 다른 스레드가 락을 획득할 수 있게 되어, 스레드 B가 대기를 해제하고 처리를 재개한다. 결과적으로, 의도한 대로 올바른 동작이 된다.[1]

이상의 처리를 시간에 따라 정리한 것이 다음 표이다. 단, 임계 구역은 '''CS'''로 줄여 썼다.[1]

디스크 상의 값스레드 A(값)스레드 B(값)CS의 소유자
100스레드 발생
100CS에 들어감스레드 A
100처리 1(100)
100처리 2(101)
100대기스레드 발생
100CS에 들어가는 데 실패
101처리 3(101)대기
101CS에서 나와 스레드 종료
101CS에 들어감스레드 B
101처리 1(101)
101처리 2(102)
102처리 3(102)
102CS에서 나와 스레드 종료


2. 2. 다른 예시



서로 다른 코드 또는 프로세스는 동일한 변수를 읽거나 써야 하지만, 그 결과가 작업의 순서에 따라 달라지는 다른 자원을 포함할 수 있다. 예를 들어, 변수 x가 프로세스 A에 의해 읽혀야 하고, 프로세스 B가 동시에 변수 x에 써야 한다면, 프로세스 A는 x의 이전 값이나 새 값을 얻을 수 있다.

'''프로세스 A:'''

```

// 프로세스 A

.

.

b = x + 5; // 명령 실행 시간 = Tx

.

```

'''프로세스 B:'''

```

// 프로세스 B

.

.

x = 3 + z; // 명령 실행 시간 = Tx

.

```

세분화된 잠금 메커니즘이 필요하지 않은 경우, 임계 구역이 중요하다. 위의 경우, A가 x의 업데이트된 값을 읽어야 하는 경우, 프로세스 A와 프로세스 B를 동시에 실행하면 원하는 결과를 얻지 못할 수 있다. 이를 방지하기 위해 변수 x는 임계 구역에 의해 보호된다. 먼저, B가 해당 구역에 접근한다. B가 값을 쓰는 것을 마치면, A가 임계 구역에 접근할 수 있으며, 변수 x를 읽을 수 있다.

임계 구역 내부와 외부에서 어떤 변수가 수정되는지 신중하게 제어함으로써 공유 변수에 대한 동시 접근을 방지할 수 있다. 임계 구역은 일반적으로 다중 스레드 프로그램이 해당 데이터에 충돌하는 변경을 하는 별도의 스레드 없이 여러 관련 변수를 업데이트해야 할 때 사용된다. 관련 상황에서 임계 구역은 프린터와 같은 공유 리소스에 한 번에 하나의 프로세스만 접근할 수 있도록 보장하는 데 사용될 수 있다.

3. 임계 구역 문제

임계 구역 문제란 임계 구역으로 지정되어야 할 코드 영역이 임계 구역으로 지정되지 않았을 때 발생할 수 있는 문제를 말한다.

서로 다른 코드 또는 프로세스는 동일한 변수를 읽거나 써야 하지만, 그 결과가 작업의 순서에 따라 달라지는 다른 자원을 포함할 수 있다. 예를 들어, 변수 x가 프로세스 A에 의해 읽혀야 하고, 프로세스 B가 동시에 변수 x에 써야 한다면, 프로세스 A는 x의 이전 값이나 새 값을 얻을 수 있다.

'''프로세스 A:'''

```

// 프로세스 A

.

.

b = x + 5; // 명령 실행 시간 = Tx

.

```

'''프로세스 B:'''

```

// 프로세스 B

.

.

x = 3 + z; // 명령 실행 시간 = Tx

.

```

세분화된 잠금 메커니즘이 필요하지 않은 경우, 임계 구역이 중요하다. 위의 경우, A가 x의 업데이트된 값을 읽어야 하는 경우, 프로세스 A와 프로세스 B를 동시에 실행하면 원하는 결과를 얻지 못할 수 있다. 이를 방지하기 위해 변수 x는 임계 구역에 의해 보호된다. 먼저, B가 해당 구역에 접근한다. B가 값을 쓰는 것을 마치면, A가 임계 구역에 접근할 수 있으며, 변수 x를 읽을 수 있다.

임계 구역 내부와 외부에서 어떤 변수가 수정되는지 신중하게 제어함으로써 공유 변수에 대한 동시 접근을 방지할 수 있다. 임계 구역은 일반적으로 다중 스레드 프로그램이 해당 데이터에 충돌하는 변경을 하는 별도의 스레드 없이 여러 관련 변수를 업데이트해야 할 때 사용된다. 관련 상황에서 임계 구역은 프린터와 같은 공유 리소스에 한 번에 하나의 프로세스만 접근할 수 있도록 보장하는 데 사용될 수 있다.

4. 임계 구역의 구현

임계 구역의 구현은 운영 체제마다 다르다. 일반적으로 임계 구역은 정해진 시간 안에 종료되며, 스레드, 태스크, 프로세스는 진입을 위해 일정 시간 동안 기다려야 한다(제한된 대기).[2]

임계 구역은 상호 배제를 필요로 하는 프로그램의 일부이며, 이를 위해 프로그램 진입 및 종료 시점에 동기화 메커니즘이 필요하다.

여러 스레드에서 잠금 및 임계 구역


위 그림에서처럼,[3] 상호 배제(뮤텍스)를 위해 하나의 스레드가 잠금 기술을 사용하여 임계 구역을 차단하면, 다른 스레드는 차례를 기다려야 한다. 이는 여러 스레드가 동일한 메모리 공간을 공유하고 공통 자원에 접근할 때 충돌을 방지한다.[2]

임계 구역 구현을 위한 의사 코드


단일 프로세서 시스템에서 임계 구역 내 프로세서 제어 변경을 막는 간단한 방법은 세마포어를 구현하는 것이다. 임계 구역 진입 시 인터럽트를 비활성화하고, 구역 내에서 컨텍스트 스위치를 유발할 수 있는 시스템 호출을 피하며, 종료 시 인터럽트를 복원한다. 이 구현에서는 시스템의 모든 임계 구역에 진입하는 실행 스레드가 원래 스레드가 해당 임계 구역을 벗어날 때까지 다른 스레드(인터럽트 포함)가 CPU에서 처리 시간을 얻는 것을 방지한다.

이 방식은 세마포어를 사용하여 개선할 수 있다. 스레드는 임계 구역 진입을 위해 세마포어를 획득하고, 구역을 벗어날 때 해제한다. 다른 스레드는 동시 진입이 방지되지만, CPU 제어를 얻어 다른 코드를 실행할 수 있다. 세마포어 잠금에는 교착 상태를 방지하기 위한 시간 제한이 있다.

4. 1. 코드 구역

각 프로세스는 자신의 임계 구역에 진입하려면 진입 허가를 요청해야 한다. 이러한 요청을 구현하는 코드 부분을 '''입장 구역'''이라고 한다. 입장 구역에서 기다리다가 진입 허가가 나면 임계 구역에 들어간다. 임계 구역 이후에는 임계 구역을 빠져나왔음을 알리는 코드 부분인 '''퇴장 구역'''이 있다. 그 밖의 나머지 코드 부분들을 총칭하여 '''나머지 구역'''이라 한다.[2]

어떤 스레드가 배타적 제어를 한 임계 구역에 들어가 있는 동안, 다른 스레드는 임계 구역에 들어갈 수 없다. 보통 그 스레드는 대기 상태가 된다.

그림에서 보듯이,[3] 상호 배제(뮤텍스)의 경우, 하나의 스레드는 공유 자원에 접근해야 할 때 잠금 기술을 사용하여 임계 구역을 차단하고, 다른 스레드는 해당 구역에 진입하기 위해 차례를 기다려야 한다. 이것은 둘 이상의 스레드가 동일한 메모리 공간을 공유하고 공통 자원에 접근하려 할 때 충돌을 방지한다.[2]

이상의 처리를 시간에 따라 정리한 표는 다음과 같다.

디스크 상의 값스레드 A(값)스레드 B(값)임계 구역(CS)의 소유자
100스레드 발생
100CS에 들어감스레드 A
100처리 1(100)
100처리 2(101)
100대기스레드 발생
100CS에 들어가는 데 실패
101처리 3(101)대기
101CS에서 나와 스레드 종료
101CS에 들어감스레드 B
101처리 1(101)
101처리 2(102)
102처리 3(102)
102CS에서 나와 스레드 종료


4. 2. 동기화 메커니즘

임계 구역은 프로그램에서 동시에 여러 스레드가 접근했을 때 문제가 발생할 수 있는 영역이다. 이러한 문제를 해결하기 위해 동기화 메커니즘이 사용된다.

임계 구역의 독점적인 사용을 보장하기 위해 프로그램 진입점과 종료 시점에 동기화 메커니즘이 필요하다.[2]

위 그림에서 볼 수 있듯이,[3] 상호 배제 (뮤텍스)의 경우, 하나의 스레드는 공유 자원에 접근해야 할 때 잠금 기술을 사용하여 임계 구역을 차단하고, 다른 스레드는 해당 구역에 진입하기 위해 차례를 기다려야 한다. 이것은 둘 이상의 스레드가 동일한 메모리 공간을 공유하고 공통 자원에 접근하려 할 때 충돌을 방지한다.[2]

  • 세마포어(Semaphore): 임계 구역에 접근할 수 있는 스레드의 수를 제어한다. 스레드는 임계 구역에 진입하기 위해 세마포어를 획득해야 하며, 구역을 벗어날 때 세마포어를 해제한다. 이를 통해 여러 스레드가 동시에 임계 구역에 접근하는 것을 방지하고, 교착 상태(deadlock)를 방지하기 위해 시간 제한을 설정할 수 있다.
  • 뮤텍스(Mutex): 한 번에 하나의 스레드만 임계 구역에 접근할 수 있도록 하는 상호 배제(Mutual Exclusion) 기법이다.
  • 잠금(Lock): 특정 자원에 대한 접근 권한을 제어하는 메커니즘으로, 배타적 제어에서 사용 권한을 의미한다.


웹 페이지 방문자 수를 나타내는 카운터 프로그램을 예로 들어 설명한다.

배타적 제어를 사용한 임계 구역은 '''하나의 스레드만이 사용 권한을 얻을 수 있는 프로그램상의 처리 영역'''이다. 이 사용 권한은 잠금(lock)이라고 불리기도 한다.

어떤 스레드가 배타적 제어를 한 임계 구역에 들어가 있는 동안, 다른 스레드는 임계 구역에 들어갈 수 없다. 보통 그 스레드는 대기 상태가 된다.

이 카운터 프로그램의 경우, 프로그램 시작 부분, 즉 처리 1 전에 '''배타적 제어의 잠금을 획득하여 임계 구역에 들어가는''' 처리를 추가할 필요가 있다. 그리고 스레드가 종료되기 전에 '''배타적 제어의 잠금을 해제하여 임계 구역에서 나오는''' 처리를 추가하면 완료된다.

이상의 처리를 시간에 따라 정리한 것이 다음 표이다.

디스크 상의 값스레드 A(값)스레드 B(값)CS의 소유자
100스레드 발생
100CS에 들어감스레드 A
100처리 1(100)
100처리 2(101)
100대기스레드 발생
100CS에 들어가는 데 실패
101처리 3(101)대기
101CS에서 나와 스레드 종료
101CS에 들어감스레드 B
101처리 1(101)
101처리 2(102)
102처리 3(102)
102CS에서 나와 스레드 종료



멀티태스킹 및 멀티스레드를 지원하는 플랫폼(운영 체제)이나 프로그래밍 언어의 표준 라이브러리에는, 스레드 간 또는 프로세스 간의 임계 구역의 상호 배제를 실현하기 위한 API가 준비되어 있다.

예를 들어 Microsoft Windows에서는, 스레드 간의 상호 배제 제어에 사용하는 Windows API로서, `CRITICAL_SECTION`구조체나, `EnterCriticalSection()`, `LeaveCriticalSection()` 함수 등이 준비되어 있다[7]. 또한 Windows에서는 프로세스 간의 상호 배제 제어에 뮤텍스를 사용한다[8].

POSIX 스레드(Pthreads)에서는 스레드 간의 상호 배제 제어에 뮤텍스 `pthread_mutex_t`를 사용한다.

4. 3. 인터럽트 비활성화

단일 프로세서 시스템에서 임계 구역을 구현하는 가장 간단한 방법은 임계 구역에 진입할 때 인터럽트를 비활성화하고, 구역 내에서 컨텍스트 스위치를 유발할 수 있는 시스템 호출을 피하며, 종료 시 인터럽트를 이전 상태로 복원하는 것이다.[2] 이 방법을 사용하면, 시스템의 모든 임계 구역에 진입하는 스레드는 해당 임계 구역을 벗어날 때까지 다른 스레드(인터럽트 포함)가 CPU에서 처리 시간을 얻는 것을 막는다.

이러한 방식은 세마포어를 사용하여 개선할 수 있다. 스레드는 임계 구역에 진입하기 위해 세마포어를 획득하고, 구역을 벗어날 때 해제한다. 다른 스레드는 원래 스레드와 동시에 임계 구역에 진입하는 것이 방지되지만, CPU 제어를 얻어 다른 코드를 실행할 수 있다. 세마포어 잠금에는 교착 상태를 방지하기 위한 시간 제한이 있다.

5. 임계 구역의 활용

임계 구역은 커널 수준, 자료 구조, 주변 장치 등 다양한 분야에서 활용된다.

커널 수준에서 임계 구역은 프로세서 간 스레드 및 프로세스 마이그레이션을 방지하고, 인터럽트 및 다른 프로세스에 의한 선점을 막는 데 사용된다.[4] 스케줄러는 임계 구역에서 실행 중인 프로세스나 스레드가 완료될 때까지 실행을 허용하거나, 다른 퀀텀(quantum)을 위해 스케줄링한다. 임계 구역은 진입한 프로세서에서만 실행되므로, 프로세서 간 동기화는 필요하지 않다.[5]

자료 구조 측면에서, 임계 구역은 연결 리스트, 트리, 해시 테이블과 같이 여러 스레드가 동시에 접근할 때 발생할 수 있는 데이터 충돌 문제를 해결하는 데 사용된다.[6] 예를 들어, 한 스레드에서 요소를 검색하는 동안 다른 스레드에서 해당 요소를 삭제하면 오류가 발생할 수 있는데, 임계 구역을 사용하여 이러한 문제를 방지하고 코드가 예상대로 작동하도록 할 수 있다.[6]

주변 장치 제어에도 임계 구역이 필요하다. 여러 프로세스가 동시에 장치를 제어하려고 할 때, 잘못된 동작이 발생할 수 있기 때문이다. 임계 구역을 통해 한 번에 하나의 프로세스만 장치에 접근하도록 보장하여 이러한 문제를 해결한다. 저장 장치 또한 임계 구역 개념이 적용되는 예시 중 하나이며, 파일에 대한 여러 접근 또는 업데이트 작업은 파일 잠금 메커니즘으로 보호되어야 한다.

5. 1. 커널 수준 임계 구역

임계 구역은 프로세서 간의 스레드 및 프로세스 마이그레이션을 방지하고, 인터럽트 및 다른 프로세스, 스레드에 의한 프로세스 및 스레드의 선점을 방지한다.[4]

임계 구역은 종종 중첩을 허용하며, 이를 통해 거의 비용 없이 여러 임계 구역에 들어가고 나올 수 있다.

스케줄러가 임계 구역에서 현재 프로세스 또는 스레드를 인터럽트하면, 스케줄러는 현재 실행 중인 프로세스 또는 스레드가 임계 구역 완료까지 실행되도록 허용하거나, 다른 완전한 퀀텀(quantum)을 위해 프로세스 또는 스레드를 스케줄링한다. 스케줄러는 현재 프로세스 또는 스레드가 임계 구역에 있는 동안 프로세스 또는 스레드를 다른 프로세서로 마이그레이션하지 않으며, 다른 프로세스 또는 스레드를 실행하도록 스케줄링하지 않는다.[4]

인터럽트가 임계 구역에서 발생하면, 인터럽트 정보가 나중 처리를 위해 기록되고, 실행이 임계 구역의 프로세스 또는 스레드로 반환된다.[4] 임계 구역이 종료되면, 경우에 따라 스케줄링된 퀀텀이 완료되고, 보류 중인 인터럽트가 실행된다. 스케줄링 퀀텀의 개념은 "라운드 로빈" 및 유사한 스케줄링 정책에 적용된다.

임계 구역은 진입한 프로세서에서만 실행될 수 있으므로, 동기화는 실행 중인 프로세서 내에서만 필요하다. 이를 통해 임계 구역에 거의 비용 없이 들어가고 나올 수 있다. 프로세서 간의 동기화는 필요하지 않다. 명령 스트림 동기화만 필요하다.[5] 대부분의 프로세서는 현재 실행 상태를 인터럽트하여 필요한 양의 동기화를 제공한다. 이를 통해 대부분의 경우 임계 구역은 프로세서별로 진입한 임계 구역의 개수만 세는 것으로 충분하다.

성능 향상에는 모든 임계 구역의 종료 시 보류 중인 인터럽트를 실행하고, 모든 임계 구역의 종료 시 스케줄러가 실행되도록 허용하는 것이 포함된다. 또한, 보류 중인 인터럽트는 실행을 위해 다른 프로세서로 전송될 수 있다.

임계 구역은 오래 지속되는 잠금 기본 요소로 사용해서는 안 된다. 임계 구역은 하드웨어 및 스케줄러로부터 어떠한 인터럽트도 발생하지 않고 들어가고, 실행되고, 종료될 수 있을 정도로 짧게 유지되어야 한다.

커널 레벨 임계 구역은 소프트웨어 잠금 문제의 기초가 된다.

5. 2. 자료 구조

병렬 프로그래밍에서 코드는 스레드로 분할된다. 읽기-쓰기 충돌 변수는 스레드 간에 분할되고 각 스레드는 해당 변수의 복사본을 갖는다. 연결 리스트, 트리 및 해시 테이블과 같은 자료 구조는 스레드 간에 분할할 수 없는 연결된 데이터 변수를 갖기 때문에 병렬성을 구현하는 것은 매우 어렵다.[6] 자료 구조 구현의 효율성을 향상시키기 위해 삽입, 삭제 및 검색과 같은 여러 작업을 병렬로 실행할 수 있다. 이러한 작업을 수행하는 동안 동일한 요소를 한 스레드에서 검색하고 다른 스레드에서 삭제하는 상황이 발생할 수 있다. 이러한 경우 출력은 오류가 발생할 수 있다. 요소를 검색하는 스레드는 찾았을 수 있지만, 다른 스레드는 이후에 해당 요소를 삭제할 수 있다. 이러한 상황은 잘못된 데이터를 제공하여 실행 중인 프로그램에 문제를 일으킨다. 이를 방지하는 한 가지 방법은 전체 자료 구조를 임계 구역 아래에 두어 한 번에 하나의 작업만 처리하도록 하는 것이다. 또 다른 방법은 사용 중인 노드를 임계 구역 아래에 잠가 다른 작업에서 동일한 노드를 사용하지 않도록 하는 것이다. 따라서 임계 구역을 사용하면 코드가 예상된 출력을 제공한다.[6]

5. 3. 주변 장치

주변 장치와 같은 외부 I/O 장치를 조작하는 코드에서도 임계 구역이 발생한다. 주변 장치의 레지스터는 특정 순서로 특정 값으로 프로그래밍되어야 한다. 둘 이상의 프로세스가 장치를 동시에 제어하는 경우, 어떤 프로세스도 장치를 필요한 상태로 만들지 못해 잘못된 동작이 발생한다.

여러 개의 출력 작업을 실행하여 복잡한 정보 단위를 출력 장치에서 생성해야 할 때는, 다른 프로세스가 자체 출력 비트를 섞어 데이터를 손상시키지 않도록 독점적인 접근이 필요하다.

입력 방향에서는, 여러 개의 개별 입력 작업을 통해 복잡한 데이터를 읽을 때 독점적인 접근이 필요하다. 이렇게 하면 다른 프로세스가 일부 조각을 소비하여 손상을 일으키는 것을 방지할 수 있다.

저장 장치는 일종의 메모리를 제공한다. 임계 구역의 개념은 메인 메모리의 공유 데이터 구조와 마찬가지로 저장 장치에도 관련이 있다. 파일에 대한 여러 접근 또는 업데이트 작업을 수행하는 프로세스는 적절한 파일 잠금 메커니즘으로 보호해야 하는 임계 구역을 실행하는 것이다.

6. API

멀티태스킹 및 멀티스레드를 지원하는 플랫폼(운영 체제)이나 프로그래밍 언어의 표준 라이브러리에는, 스레드 간 또는 프로세스 간의 임계 구역의 상호 배제를 실현하기 위한 API가 준비되어 있다.

6. 1. 예시

웹 페이지 방문자 수를 나타내는 카운터 프로그램을 예로 들어 설명한다. 카운터 프로그램은 대략적으로 다음 처리를 수행한다.

# 디스크 등의 기억 장치 상의 파일에서 현재 카운터 값을 메모리로 읽어온다.

# 메모리 상에서 카운터 값을 1 증가시킨다.

# 카운터 값을 메모리에서 기억 장치로 다시 기록한다.

실제로는 이러한 처리 각각이 여러 개의 세부적인 명령 집합으로 구성되는 것이 일반적이다.

프로그램의 프로세스는 항상 하나만 실행되지만, 개별적인 액세스는 태스크화되어 이를 처리하는 여러 스레드로 분배되는 것으로 한다. 멀티태스킹 및 멀티스레드를 지원하는 플랫폼(운영 체제)이나 프로그래밍 언어의 표준 라이브러리에는, 스레드 간 또는 프로세스 간의 임계 구역의 상호 배제를 실현하기 위한 API가 준비되어 있다.

예를 들어 Microsoft Windows에서는, 스레드 간의 상호 배제 제어에 사용하는 Windows API로서, `CRITICAL_SECTION`구조체나, `EnterCriticalSection()`, `LeaveCriticalSection()` 함수 등이 준비되어 있다[7]. 또한 Windows에서는 프로세스 간의 상호 배제 제어에 뮤텍스를 사용한다[8]

POSIX 스레드(Pthreads)에서는 스레드 간의 상호 배제 제어에 뮤텍스 `pthread_mutex_t`를 사용하지만, 이것은 Windows에서의 뮤텍스와는 다른 개념이다. `pthread_mutexattr_setpshared()` 함수에서 `PTHREAD_PROCESS_SHARED`를 지정함으로써, 프로세스 간의 상호 배제 제어에 `pthread_mutex_t`를 사용할 수 있게 되는 환경도 있다.

Java나 C# 등, 후발 언어에는 구문 자체에 임계 구역의 스레드 간 상호 배제 제어를 기술하기 위한 기능이 내장되어 있는 것도 있다.

참조

[1] 서적 Concurrent Programming: Algorithms, Principles, and Foundations Springer Science & Business Media
[2] 서적 GNU/Linux Application Programming (2nd ed.). [Hingham, Mass.] Charles River Media
[3] 서적 2012 International Conference for High Performance Computing, Networking, Storage and Analysis 2012-11-10
[4] 간행물 RESEARCH PAPER ON SOFTWARE SOLUTION OF CRITICAL SECTION PROBLEM https://www.research[...] 2011-11
[5] 간행물 Synchronization, Coherence, and Event Ordering in Multiprocessors
[6] 서적 Fundamentals of Parallel Multicore Architecture Taylor & Francis 2015-11-17
[7] 웹사이트 Critical Section Objects - Windows applications | Microsoft Docs https://docs.microso[...]
[8] 웹사이트 Mutex Objects - Windows applications | Microsoft Docs https://docs.microso[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com