원자 값 교환: atomic_exchange(C11)

원자 값 교환: atomic_exchange(C11)

원자 객체의 값을 새 값으로 바꾸면서, 바꾸기 전에 담겨 있던 이전 값을 돌려받고 싶을 때가 있어요. 스레드가 상태 플래그 하나를 이리저리 넘겨받을 때 자주 쓰는 패턴이죠. 이 두 가지를 한 번에 원자적으로 처리해 주는 함수가 **atomic_exchange**예요.

출처: cppreference

본문

시그니처

<stdatomic.h> 헤더에 정의돼 있어요.

형태 주석
C atomic_exchange(volatile A* obj, C desired); (1) C11부터
C atomic_exchange_explicit(volatile A* obj, C desired, memory_order order); (2) C11부터

동작 방식

obj가 가리키는 값을 desired로 원자적으로 바꾸고, 그전에 obj가 담고 있던 값을 반환해요. read-modify-write 연산이에요. (1) 버전은 memory_order_seq_cst로, (2) 버전은 order로 메모리 접근을 정렬해요.

이 함수는 모든 원자 객체 타입 A에 대해 정의된 제네릭 함수예요. 인자는 volatile 원자 타입의 포인터라서 비휘발성·휘발성(예: 메모리 매핑 I/O) 원자 객체의 주소를 모두 받아들여요. CA에 대응하는 비원자 타입이에요.

제네릭 함수의 이름이 매크로인지 외부 연결을 가진 식별자인지는 규정되지 않았어요. 매크로 정의를 막고 실제 함수에 접근하려고 (atomic_exchange)(...)처럼 괄호로 감싸 쓰는 경우, 또는 프로그램이 제네릭 함수 이름과 같은 외부 식별자를 정의하는 경우의 동작은 미정의(undefined)예요.

매개변수

매개변수 설명
obj 수정할 원자 객체를 가리키는 포인터
desired 원자 객체를 교체할 값
order 이 연산의 메모리 동기화 순서. 모든 값 허용

반환값

obj가 이전에 담고 있던 값을 반환해요.

예제

원자 정수에 차례대로 새 값을 저장하면서, 교환 전의 값을 돌려받는 모습을 상상해 볼게요. 예를 들어 어떤 원자 상태 변수의 값을 바꾸되, 바꾸기 전에 무엇이었는지를 기록으로 남기고 싶다면 atomic_exchange가 이전 값을 돌려주기 때문에 그대로 쓸 수 있어요. 조건부로 바꾸고 싶다면 이 함수 대신 atomic_compare_exchange 계열을 써야 합니다.

더 알아보기

  • atomic_compare_exchange: 기대값이 맞을 때만 교환.
  • atomic_fetch_addatomic_fetch_*: 값을 계산해 저장하고 이전 값을 반환.
  • memory_order: 메모리 동기화 순서.