Automic

2025. 3. 20. 21:42High Level Programming Language

다음과 같은 코드가 있습니다.

int a = 1;
int b = 2;
a = b;

 

위 코드를 Intel CPU의 x86 어셈블리어로 변환하면, a = b; 코드는 메모리에서 값을 로드하고 저장하는 명령어로 변환됩니다.

일반적인 32비트 x86 어셈블리어 코드로 표현하면 다음과 같습니다.

mov eax, DWORD PTR [b]   ; 변수 b의 값을 레지스터 eax에 로드
mov DWORD PTR [a], eax   ; eax의 값을 변수 a에 저장

64비트 환경에서는 64비트 레지스터를 사용할 수도 있습니다.

mov rax, QWORD PTR [b]   ; 변수 b의 값을 rax 레지스터에 로드
mov QWORD PTR [a], rax   ; rax의 값을 변수 a에 저장

여기서 DWORD PTRQWORD PTR은 32비트 또는 64비트 변수를 다룬다는 의미입니다.

만약 컴파일러가 최적화를 수행하면, mov eax, [b]와 같은 더 간단한 형태로 변환될 수도 있습니다.

 

그런데, a = b;는 일반적으로 원자적(atomic) 연산이 아닙니다. 즉, 이 코드가 실행되는 동안 다른 스레드가 개입할 가능성이 있으며, 원자성이 보장되지 않습니다.

이유

  1. 메모리 접근 과정
    • a = b;는 두 단계의 명령어로 실행됩니다.
      1. b의 값을 읽어 레지스터에 로드 (mov eax, [b])
      2. eax의 값을 a에 저장 (mov [a], eax)
    • CPU가 이 두 단계 사이에 다른 작업을 수행할 수도 있습니다.
  2. 멀티스레드 환경에서의 문제
    • 다른 스레드가 b를 변경하거나, a를 읽는 경우 예기치 않은 동작이 발생할 수 있습니다.
    • 예를 들어, 첫 번째 단계(mov eax, [b])와 두 번째 단계(mov [a], eax) 사이에 다른 스레드가 b를 변경하면, a에는 예상과 다른 값이 저장될 수 있습니다.

원자성을 보장하는 방법

  1. volatile 사용 (하지만 완벽한 해결책은 아님)
    • Java의 volatile 키워드는 가시성(visibility)은 보장하지만 원자성(atomicity)은 보장하지 않습니다.
  2. synchronized 사용 (Java에서)
    • synchronized 블록을 사용하면 해당 코드 블록이 단일 스레드에서만 실행되도록 보장할 수 있습니다.
  3. std::atomic 사용 (C++에서)
    • C++에서는 std::atomic<int>를 사용하면 원자적 연산이 가능합니다.
  4. CPU의 원자적 명령어 사용 (LOCK prefix, xchg)
    • x86 CPU에서는 LOCK 접두사를 사용한 mov 또는 xchg 명령어를 활용하여 원자성을 보장할 수 있습니다.
    • 예제:
      mov eax, [b]  ; b 값을 읽음
      lock xchg [a], eax  ; a와 eax를 원자적으로 교환
  5. 메모리 배리어 사용 (Memory Barrier, Fence)
    • 특정 플랫폼에서는 메모리 배리어(MFENCE, SFENCE, LFENCE)를 사용하여 CPU의 재정렬을 방지할 수도 있습니다.

결론

특정 코드 수정 없이 a = b;를 실행하면 원자성이 항상 보장되지 않습니다. 원자성이 필요한 경우, synchronized, std::atomic, 혹은 CPU의 원자적 명령어를 사용해야 합니다.

'High Level Programming Language' 카테고리의 다른 글

Namespace  (1) 2025.02.23
java package-qualified class name  (0) 2025.01.16
Java Advanced Programming Quiz 문제  (0) 2024.07.23
Java Tutorials  (0) 2024.06.25
고급 프로그래밍 언어(High Level Programming Language)  (0) 2024.06.23