락 경합

AI
gemma-4-31b
작성자
익명
작성일
2026.08.05
조회수
2
버전
v2

📋 문서 버전

이 문서는 2개의 버전이 있습니다. 현재 최신 버전을 보고 있습니다.

락 경합 (Lock Contention)

1. 개요

락 경합(Lock Contention)이란 멀티스레드 환경에서 여러 스레드가 동일한 공유 자원에 접근하기 위해 하나의 락(Lock, 상호 배제 메커니즘)을 동시에 획득하려고 시도할 때 발생하는 충돌 현상을 의미한다. 락은 데이터의 일관성을 유지하기 위해 필수적이지만, 경합이 심화될수록 스레드들이 락 획득을 위해 대기하는 시간이 늘어나며 이는 시스템 전체의 처리량 저하와 응답 시간 증가라는 성능 병목 현상으로 이어진다.

2. 발생 원리와 메커니즘

락 경합은 기본적으로 상호 배제(Mutual Exclusion) 원칙에 의해 발생한다. 특정 스레드가 임계 구역(Critical Section, 공유 자원에 접근하는 코드 영역)에 진입하여 락을 점유하면, 다른 스레드들은 해당 락이 해제될 때까지 대기 상태로 전환된다.

이 과정에서 가장 큰 오버헤드는 컨텍스트 스위칭(Context Switching)에서 발생한다. 대기 상태로 전환된 스레드는 CPU 스케줄러에 의해 실행 큐에서 제외(Suspend)되었다가, 락이 해제되어 다시 실행 가능 상태가 되면 다시 CPU 할당을 받아야 한다. 이 전환 과정에서 레지스터 상태 저장 및 복구, 캐시 미스(Cache Miss) 등이 발생하여 실제 연산 시간보다 관리 비용이 더 커지는 상황이 초래된다.

[표 1] 락 획득 성공 시 vs 경합 발생 시 스레드 상태 변화

구분 락 획득 성공 (No Contention) 락 경합 발생 (Contention)
스레드 상태 Running $\rightarrow$ Running Running $\rightarrow$ Blocked/Waiting $\rightarrow$ Runnable $\rightarrow$ Running
CPU 동작 중단 없이 임계 구역 실행 컨텍스트 스위칭 발생 $\rightarrow$ 대기 $\rightarrow$ 재스케줄링
오버헤드 매우 낮음 (단순 락 획득/해제 비용) 매우 높음 (커널 모드 전환 및 스케줄링 비용)
실행 흐름 선형적 실행 불연속적 실행 및 지연 발생

3. 락 경합과 데드락의 차이점

락 경합과 데드락(Deadlock, 교착 상태)은 모두 동기화 문제에서 발생하지만, 그 성격과 결과는 완전히 다르다.

  • 락 경합 (Lock Contention): 성능의 문제이다. 스레드가 락을 얻기 위해 '기다리는' 상태이며, 시간이 지나 락이 해제되면 결국 작업이 완료된다. 즉, 느려지는 것이 핵심이다.
  • 데드락 (Deadlock): 논리적 오류의 문제이다. 두 개 이상의 스레드가 서로가 가진 락을 기다리며 무한히 대기하는 상태이다. 외부의 강제 종료 없이는 절대 해결되지 않으며, 멈추는 것이 핵심이다.

4. 락 경합의 영향 및 증상

락 경합이 심해지면 시스템은 다음과 같은 역설적인 성능 지표를 보인다.

  1. CPU 자원 효율성 저하 및 처리량(Throughput) 감소: 스레드들이 연산을 수행하지 못하고 대기하므로 실제 단위 시간당 처리하는 요청 수는 급격히 떨어진다. 이때 뮤텍스(Mutex) 기반의 대기 시에는 CPU 사용률이 낮아질 수 있으나, 스핀락(Spin-lock) 기반의 경합 시에는 CPU가 계속 루프를 돌기 때문에 오히려 CPU 사용률이 100%로 치솟는 상반된 현상이 나타날 수 있다.
  2. 응답 시간(Latency)의 급증: 락을 획득하기 위한 대기 시간이 응답 시간에 합산되어, 사용자 체감 속도가 느려진다.
  3. 컨텍스트 스위칭 횟수 증가: OS 레벨에서 스레드 전환이 빈번하게 일어나며 시스템 커널의 부하가 증가한다.

5. 락 경합 해결 및 최적화 전략

경합을 줄이기 위한 핵심은 "락의 점유 시간을 최소화하고, 락의 범위를 세분화하는 것"이다.

5.1 락 범위 최소화 (Lock Granularity)

락을 거는 코드 영역을 최대한 좁게 설정하여, 다른 스레드가 대기하는 시간을 줄인다.

5.2 락 분할 (Lock Stripping)

하나의 거대한 락 대신, 자원을 여러 개의 버킷으로 나누어 각각 별도의 락을 적용하는 방식이다. (예: Java의 ConcurrentHashMap)

5.3 낙관적 락 (Optimistic Locking)

데이터 충돌이 적을 것이라고 가정하고, 락을 걸지 않은 채 작업을 수행한 뒤 커밋 시점에 충돌 여부를 확인하는 방식이다. 충돌 시에만 재시도(Retry)를 수행한다.

5.4 읽기/쓰기 락 (Read-Write Lock)

읽기 작업이 많고 쓰기 작업이 적은 환경에서 사용하는 방식이다. 읽기 스레드 간에는 공유 락(Shared Lock)을 허용하여 동시에 접근하게 하고, 쓰기 스레드에게만 배타적 락(Exclusive Lock)을 부여함으로써 경합을 획기적으로 줄인다.

[코드 예제] 락 범위 비교 (Java 스타일)

Bad: 거대한 락 (Coarse-grained Lock)

public synchronized void updateData() {
    // 1. 무거운 DB 조회 (락 점유 중)
    // 2. 복잡한 비즈니스 로직 계산 (락 점유 중)
    // 3. 데이터 수정 (락 점유 중)
    // 결과: 모든 스레드가 1~3번 과정이 끝날 때까지 대기함
}

Good: 세분화된 락 (Fine-grained Lock)

public void updateData() {
    // 1. 무거운 DB 조회 (락 없이 수행)
    Data data = fetchData(); 
    
    synchronized(lockObject) {
        // 2. 최소한의 수정 작업만 락 범위 내에서 수행
        data.setValue(newValue);
    }
    // 3. 결과 반영 (락 없이 수행)
}

6. 대안 기술: Non-blocking 알고리즘

락 자체를 사용하지 않고 동시성을 제어하는 기법으로, 경합 문제를 근본적으로 해결하려는 시도이다.

  • CAS (Compare-And-Swap): 메모리의 현재 값과 내가 예상한 값이 일치할 때만 새로운 값으로 교체하는 원자적(Atomic) 연산이다. 락을 걸어 다른 스레드를 멈추게 하는 대신, 업데이트 직전에 값이 변경되었는지 확인하고 실패 시 다시 시도하는 '낙관적' 방식을 취함으로써 컨텍스트 스위칭 비용을 제거한다. 다만, 값이 A $\rightarrow$ B $\rightarrow$ A로 변했을 때 변경되지 않은 것으로 인식하는 ABA 문제가 발생할 수 있으며, 이는 버전 관리(Versioning)나 스탬프를 통해 해결한다.
  • Lock-free: 최소한 하나의 스레드는 항상 전진함을 보장하는 구조이다.
  • Wait-free: 모든 스레드가 유한한 단계 내에 작업을 완료함을 보장하는 가장 강력한 수준의 Non-blocking 구조이다.

7. 특수 락: 스핀락 (Spinlock)

스핀락은 락을 획득할 때까지 스레드를 대기 상태(Blocked)로 전환하지 않고, 루프를 돌며 계속해서 락 획득을 시도하는 방식이다.

  • 특징: 컨텍스트 스위칭 비용이 발생하지 않으므로, 락 점유 시간이 매우 짧을 때 효율적이다. 하지만 락을 얻지 못하는 동안 CPU를 계속 점유하므로 점유 시간이 길어지면 CPU 낭비가 심해진다.
  • 사용 사례: OS 커널의 인터럽트 핸들러, 매우 짧은 임계 구역을 가진 저수준 드라이버 등 컨텍스트 스위칭 오버헤드가 실제 대기 시간보다 더 큰 환경에서 주로 사용된다.

8. 언어별 락 구현체 비교

각 언어는 락 경합을 효율적으로 처리하기 위해 다양한 구현체를 제공한다.

언어 주요 구현체 특징 비고
Java ReentrantLock, StampedLock 적응형 스핀락(Adaptive Spin-lock) 및 읽기/쓰기 분리 락 제공 JVM 수준의 최적화
Go sync.Mutex, channel 가벼운 고루틴(Goroutine) 기반의 스케줄링과 채널을 통한 통신 지향 CSP 모델 채택
C++ std::mutex, std::atomic OS 커널 락과 하드웨어 원자적 연산을 직접 제어 가능 저수준 제어 가능
Python threading.Lock, multiprocessing.Lock GIL로 인해 멀티스레드 환경에서 CPU 집약적 작업 시 락 경합이 필연적으로 발생함 멀티프로세싱 권장

9. 진단 및 모니터링 방법

실무에서 락 경합을 찾아내기 위해서는 다음과 같은 지표와 도구를 활용한다.

9.1 성능 측정 지표 (Metrics)

  • Lock Wait Time: 스레드가 락을 획득하기 위해 대기한 총 시간.
  • Lock Hold Time: 스레드가 락을 획득한 후 해제할 때까지 걸린 시간.
  • Context Switches per Second: 초당 발생하는 컨텍스트 스위칭 횟수 (급증 시 경합 의심).
  • Blocked Thread Count: 특정 시점에 BLOCKED 상태에 있는 스레드의 수.

9.2 진단 도구 및 방법

  1. 스레드 덤프 (Thread Dump): 현재 실행 중인 모든 스레드의 상태를 스냅샷으로 찍어, 어떤 스레드가 어떤 락을 보유하고 있고, 어떤 스레드가 그 락을 기다리고 있는지(waiting on <0x...> 형태) 분석한다.
  2. 프로파일링 도구:
    • Java: JVisualVM, JProfiler, async-profiler (Lock profiling 모드 사용).
    • Go: pprof (mutex profile을 통해 경합 지점 확인).
  3. Hotspot 분석: CPU 사용률은 낮으나 응답 시간이 튀는 특정 메서드를 추적하여 임계 구역의 병목 지점을 식별한다.

하드웨어 수준의 경합: 버스 및 메모리 인터커넥트

소프트웨어 수준의 락 경합이 스레드 스케줄링과 컨텍스트 스위칭의 문제라면, 하드웨어 수준의 경합은 CPU 코어들이 공유 메모리에 접근하기 위해 시스템 버스(System Bus)인터커넥트(Interconnect)라는 물리적 통로를 점유하려 할 때 발생한다.

멀티코어 프로세서에서 각 코어는 독립적인 L1, L2 캐시를 가지지만, 최종적으로 메인 메모리에 접근하거나 코어 간 데이터를 교환할 때는 공통의 버스를 공유한다. 특정 코어가 원자적 연산을 위해 버스를 독점하거나 대량의 데이터를 전송할 때, 다른 코어들은 물리적인 통로가 확보될 때까지 대기해야 하며 이는 대역폭 제한(Bandwidth Limitation) 문제로 이어진다. 결과적으로 소프트웨어적으로는 락-프리(Lock-free) 구조를 구현했더라도, 하드웨어 인터커넥트 수준에서 병목이 발생하면 전체 시스템 성능이 저하되는 현상이 나타난다.

캐시 일관성 프로토콜과 버스 트래픽

멀티코어 환경에서는 각 코어의 캐시에 저장된 데이터 일관성을 유지하기 위해 MESI 프로토콜과 같은 캐시 일관성 프로토콜을 사용한다. 이 과정에서 발생하는 상태 변화와 통신이 락 경합의 물리적 실체인 '버스 트래픽'을 유발한다.

MESI 프로토콜 상태 변화도

MESI는 캐시 라인의 상태를 네 가지로 구분하여 관리한다. * M (Modified): 데이터가 수정되었으며, 해당 코어의 캐시에만 최신 값이 존재함. (메모리와 불일치) * E (Exclusive): 데이터가 수정되지 않았으며, 해당 코어의 캐시에만 존재함. (메모리와 일치) * S (Shared): 데이터가 수정되지 않았으며, 여러 코어의 캐시에 복제되어 존재함. (메모리와 일치) * I (Invalid): 해당 캐시 라인의 데이터가 유효하지 않음.

[상태 변화 메커니즘] 1. Read Miss: 코어가 데이터를 읽으려 할 때 캐시에 없으면 버스를 통해 요청 $\rightarrow$ 다른 코어가 M 상태라면 메모리에 쓰고 S로 변경 $\rightarrow$ 요청 코어도 S 상태로 데이터 획득. 2. Write Hit (S $\rightarrow$ M): S 상태의 데이터를 수정하려 하면, 버스에 Invalidation(무효화) 신호를 보내 다른 모든 코어의 해당 라인을 I 상태로 변경시킨 후 자신만 M 상태가 됨.

캐시 라인 바운싱 (Cache Line Bouncing)

여러 코어가 동일한 락 변수(또는 공유 데이터)를 빈번하게 수정하려고 시도할 때, 해당 캐시 라인의 소유권이 코어 사이를 빠르게 옮겨 다니는 현상을 캐시 라인 바운싱이라고 한다. * 메커니즘: 코어 A가 쓰기 위해 M 상태 획득 $\rightarrow$ 코어 B가 쓰기 위해 A의 라인을 I로 만들고 M 상태 획득 $\rightarrow$ 코어 C가 다시 요청... 이 과정이 반복되며 버스에 무효화 트래픽이 폭증한다. * 성능 저하: 실제 연산 시간보다 캐시 라인을 동기화하기 위한 버스 통신 시간이 더 길어지며, 이는 CPU 파이프라인의 스톨(Stall)을 유발하여 심각한 성능 저하(Bus Contention)를 초래한다.

캐시 라인 바운싱 측정 지표

실제 시스템에서 이 현상을 진단하기 위해 다음과 같은 하드웨어 성능 카운터(PMU) 지표를 측정한다. * L1D_PEND_MISS.PENDING: L1 데이터 캐시 미스가 처리되지 않고 대기 중인 요청 수. * OFFCORE_RESPONSE: 코어 외부(L3 캐시 또는 메모리)에서 오는 응답 지연 시간. * HITM (Hit Modified): 다른 코어의 캐시에서 Modified 상태인 라인을 읽어올 때 발생하는 이벤트. HITM 수치가 높을수록 캐시 라인 바운싱이 심각함을 의미하며, 이는 전형적인 락 경합의 하드웨어적 증거가 된다.

하드웨어 락 메커니즘: 버스 락 vs 캐시 락

원자적 연산(Atomic Operation)을 수행할 때, CPU는 데이터의 일관성을 보장하기 위해 하드웨어 수준의 락을 사용한다. 과거에는 버스 전체를 잠그는 방식을 사용했으나, 현대의 CPU는 효율성을 위해 캐시 락 방식을 주로 사용한다.

[표 2] 버스 락(Bus Lock)과 캐시 락(Cache Lock) 비교

구분 버스 락 (Bus Lock) 캐시 락 (Cache Lock)
작동 방식 LOCK# 핀을 활성화하여 시스템 버스 전체를 점유 MESI 프로토콜을 이용하여 특정 캐시 라인만 배타적 점유
영향 범위 모든 코어의 메모리 접근이 일시적으로 차단됨 해당 캐시 라인을 공유하는 코어들만 영향을 받음
성능 오버헤드 매우 높음 (시스템 전체 병목 유발) 상대적으로 낮음 (국소적 영향)
발생 조건 데이터가 캐시 라인 경계에 걸쳐 있거나(Split Lock), 구형 CPU인 경우 데이터가 단일 캐시 라인 내에 정렬되어 있는 경우

하드웨어 관점의 락 경합 보강

원자적 연산의 물리적 메커니즘 (보강)

소프트웨어의 Atomic 연산은 내부적으로 CPU의 LOCK 접두사가 붙은 명령어로 변환된다. 이때 CPU는 위 표의 캐시 락을 통해 해당 메모리 주소가 포함된 캐시 라인을 Exclusive 또는 Modified 상태로 고정하여, 연산이 완료될 때까지 다른 코어가 해당 라인을 수정하지 못하도록 물리적으로 차단한다. 만약 데이터가 캐시 라인 경계에 걸쳐 있다면(Misaligned), CPU는 강제로 버스 락을 수행하여 시스템 전체의 메모리 트래픽을 멈추게 하므로, 성능 최적화를 위해 데이터 정렬(Alignment)이 매우 중요하다.

Non-blocking 알고리즘의 하드웨어적 한계 (보강)

CAS(Compare-And-Swap) 기반의 Lock-free 알고리즘은 컨텍스트 스위칭을 제거하지만, 하드웨어 수준의 경합까지 제거하지는 못한다. CAS 연산이 빈번하게 실패하고 재시도되는 상황에서는, 매 시도마다 캐시 라인의 소유권을 가져오기 위한 무효화(Invalidation) 폭풍이 발생한다. 이는 결과적으로 버스 트래픽을 급증시켜, 이론적인 알고리즘 복잡도와 무관하게 물리적인 하드웨어 병목으로 인해 성능이 선형적으로 증가하지 않는 지점에 도달하게 만든다.

스핀락의 버스 경합 문제 (보강)

스핀락은 단순히 CPU 사이클을 낭비하는 것에 그치지 않는다. 락 변수를 계속해서 읽고 쓰는 while(lock == 1) 루프는 지속적인 캐시 일관성 체크를 유발한다. 특히 단순 스핀락(Test-and-Set)의 경우, 매 루프마다 쓰기 시도를 하여 다른 코어의 캐시 라인을 계속 무효화시키므로, 락을 보유하고 있는 코어조차 버스 트래픽 때문에 임계 구역을 빠져나오는 속도가 느려지는 상호 간섭 현상이 발생한다. 이를 해결하기 위해 읽기만 수행하다가 값이 변했을 때만 쓰기를 시도하는 Test-and-Test-and-Set 방식이 사용된다.

AI 생성 콘텐츠 안내

이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.

주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.

이 AI 생성 콘텐츠가 도움이 되었나요?