[스핀락] 스핀락 (Spinlock)이란?

이건호·2025년 4월 8일
0

💡 스핀락 (Spinlock)

  • 멀티스레드 또는 멀티프로세싱 환경에서 공유 자원에 대한 동시 접근을 방지하기 위해 사용되는 락(Lock) 매커니즘.
  • 일반적인 뮤텍스(Mutex)와는 달리, 스핀락은 락을 획득할 때 락이 해제될 때까지 계속해서 루프를 돌며(lock busy-waiting) 확인.

📝 특징

  1. 바쁜 대기(Busy-waiting) 방식 사용
    • 락을 획득하기 위해 대기하는 스레드가 CPU를 점유하며 지속적으로 확인(바쁘게 대기)함.
    • while(lock != UNLOCKED) { /*계속 확인*/ } 형태로 구현됨.
  2. 경량화된 락
    • 컨텍스트 스위칭 없이 사용자 모드에서 락을 확인하므로 비용이 낮음.
    • 락이 짧은 시간 동안만 점유되는 경우 효율적임.
  3. 스케줄러 개입 없음
    • 운영체제의 스케줄링을 발생시키지 않으므로 매우 빠르게 락을 획득할 수 있음.
  4. 적합한 상황
    • 락 점유 시간이 매우 짧을 때 (짧은 임계 구역).
    • 멀티코어 시스템에서 락이 빈번하게 해제되는 경우.
  5. 비효율적인 상황
    • 락 점유 시간이 길어지면 CPU를 낭비하게 됨.
    • 단일 코어 환경에서는 비효율적임 (다른 스레드가 락을 해제할 기회가 없음).

📌 사용 예시 (C 언어)

#include <stdatomic.h>
#include <stdio.h>
#include <pthread.h>

#define UNLOCKED 0
#define LOCKED 1

typedef atomic_int spinlock;

void spinlock_lock(spinlock *lock) {
    while(atomic_exchange(lock, LOCKED) == LOCKED) {
        // 락이 해제될 때까지 대기 (바쁜 대기)
    }
}

void spinlock_unlock(spinlock *lock) {
    atomic_store(lock, UNLOCKED);
}

spinlock my_lock = ATOMIC_VAR_INIT(UNLOCKED);

void* critical_section(void* arg) {
    spinlock_lock(&my_lock);

    // 공유 자원 접근 (임계 구역)
    printf("Thread %d is in the critical section.\n", *(int*)arg);

    spinlock_unlock(&my_lock);
    return NULL;
}

int main() {
    pthread_t threads[4];
    int thread_ids[4] = {0, 1, 2, 3};

    for(int i = 0; i < 4; i++) {
        pthread_create(&threads[i], NULL, critical_section, &thread_ids[i]);
    }

    for(int i = 0; i < 4; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

🔍 코드 설명

  1. spinlock_lock() 함수
    • atomic_exchange()를 사용하여 락을 얻기 시도.
    • 성공할 때까지 계속해서 반복 (busy-waiting).
  2. spinlock_unlock() 함수
    • 락을 해제하기 위해 atomic_store() 사용.
  3. 멀티스레드 환경에서 락 사용
    • 각 스레드는 spinlock_lock()으로 락을 얻은 후, 공유 자원에 접근.
    • 작업이 끝나면 spinlock_unlock()으로 락을 해제.

📊 장점과 단점

✅ 장점

  • 락 획득이 매우 빠름 (스케줄러 호출 없음).
  • 멀티코어 시스템에서 효율적으로 작동.
  • 단순한 구현으로 인한 오버헤드 감소.

❌ 단점

  • CPU 자원 낭비: 락을 얻기 위해 계속 루프를 돌며 대기하기 때문에 CPU 사용률이 높음.
  • 단일 코어 환경에서는 비효율적.
  • 임계 구역이 긴 경우에는 부적합.

📌 사용 사례

  • 커널이나 드라이버 수준의 짧은 임계 구역 보호.
  • 멀티코어 환경에서의 경량화된 동기화 메커니즘.
  • 하드웨어에서 제공하는 원자적 연산(Atomic Operation)을 활용할 수 있는 상황.

📌 Spinlock 예제 (Java)

import java.util.concurrent.atomic.AtomicBoolean;

public class SpinLock {
    private final AtomicBoolean lock = new AtomicBoolean(false);

    public void lock() {
        while (!lock.compareAndSet(false, true)) {
            // Busy-waiting: Lock이 해제될 때까지 계속 확인
        }
    }

    public void unlock() {
        lock.set(false);
    }

    // 테스트를 위한 임계 구역 접근 메서드
    public void criticalSection(int threadId) {
        lock();
        try {
            System.out.println("Thread " + threadId + " is in the critical section.");
            // 임계 구역에서 수행할 작업 (예: 공유 자원 수정)
            Thread.sleep(100);  // 임계 구역 점유 시간
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
    }

    public static void main(String[] args) {
        SpinLock spinLock = new SpinLock();

        // 스레드 4개 생성
        for (int i = 0; i < 4; i++) {
            final int threadId = i;
            new Thread(() -> spinLock.criticalSection(threadId)).start();
        }
    }
}

🔍 코드 설명

  1. SpinLock 클래스
    • AtomicBoolean을 사용하여 락의 상태를 관리함.
    • compareAndSet() 메서드를 이용하여 락을 획득하거나 해제함.
    • lock() 메서드에서 busy-waiting 루프를 사용하여 락을 계속 확인함.
  2. criticalSection() 메서드
    • 임계 구역을 보호하기 위해 lock()unlock()을 사용하여 접근을 제어함.
    • Thread.sleep(100);는 임계 구역을 점유하는 시간을 의미.
  3. main() 메서드
    • 4개의 스레드를 생성하여 criticalSection()을 호출하게 함.
    • 스레드가 동시에 접근하지 못하도록 락이 보호해줌.

📊 장점과 단점

✅ 장점

  • 경량화된 락 메커니즘으로 매우 빠르게 락을 획득할 수 있음.
  • 컨텍스트 스위칭을 피할 수 있어 성능 이점이 있음.

❌ 단점

  • CPU 자원을 낭비할 수 있음 (특히 임계 구역이 길거나 점유 시간이 길 경우).
  • 단일 코어 환경에서는 비효율적임.
  • JVM의 스케줄러와 충돌할 가능성이 있음.

💡 주의 사항

  • Java에서 스핀락을 사용할 때는 특정 상황(예: 짧은 임계 구역 보호) 에서만 사용하는 것이 좋음.
  • 일반적으로 Java에서는 ReentrantLock이나 synchronized 키워드가 더 효율적일 수 있음.
  • 특히, Java의 ReentrantLock공정성(Fairness) 옵션도 제공하므로, 스핀락 대신 사용하기 적합한 경우가 많음.

✍️ 스핀락을 사용해야 할 때

  • 락을 점유하는 시간이 매우 짧을 때.
  • 멀티코어 환경에서 성능을 극대화하고자 할 때.
  • JVM의 Lock Optimization을 고려했을 때 성능을 더 높이고 싶을 때.

profile
주니어 자바 개발자 이건호입니다!

0개의 댓글