-
[컴퓨터 밑바닥의 비밀] 2.3 스레드 안전 코드Study/컴퓨터 밑바닥의 비밀 2025. 3. 1. 23:27
2.3.1 자유와 제약
- 스레드 안전을 달성하는 2가지 조건
- 전용 리소스를 사용하는 스레드는 스레드 안전을 달성할 수 있다.
- 함수의 지역 변수, 스레드 전용 저장소 등
- 공유 리소스를 사용하는 스레드는 다른 스레드에 영향을 주지 않도록 하는 대기 제약 조건에 맞게 공유 리소스를 사용하면 스레드 안정을 달성할 수 있다.
- 전용 리소스를 사용하는 스레드는 스레드 안전을 달성할 수 있다.
2.3.2 스레드 안전이란 무엇일까?
- 코드가 스레드 몇 개에서 호출되든 이 스레드들이 어떤 순서로 호출되든 간에 상관없이 올바른 결과가 나온다면, 이 코드를 '스레드 안전'이라고 말함
- 스레드의 전용 리소스와 공유 리소스에 어떤 것들이 있는지 파악해야 함
2.3.3 스레드 전용 리소스와 공유 리소스
- 스레드 전용 리소스
- 함수의 지역 변수, 스레드의 스택 영역, 스레드 전용 저장소
- 스레드 공유 리소스 (전용 리소스 외의 나머지 영역)
- 힙 영역
- 메모리의 동적 할당에 사용되는 영역
- C/C++ 언어의 malloc 함수와 new 예약어가 요청하는 메모리가 이 영역에 할당됨
- 데이터 영역
- 전역 변수가 저장되는 영역
- 코드 영역
- 실행 파일의 기계 명령어가 저장되는 영역
- 읽기 전용이므로 스레드 안전을 신경 쓸 필요 없음
- 힙 영역
- 주의)
- 공유 리소스를 사용하는 스레드는 스레드 안전을 위해 반드시 순서를 따라야 함
- 이를 위해 잠금(lock)이나 세마포어(semaphore) 같은 장치를 사용할 수 있음
2.3.4 스레드 전용 리소스만 사용하기
예시)
int func() { int a = 1; int b = 1; return a + b; }
- 전역 변수나 매개변수에 의존하지 않고 오로지 스레드 전용 리소스인 지역 변수만 사용
- 해당 변수는 실행된 후 스레드의 스택 영역에서 관리함
- 이런 코드를 무상태 함수(stateless function)라고도 하며, 스레드 안전이라는 것은 분명함
2.3.5 스레드 전용 리소스와 함수 매개변수
예시1) 스레드 안전
int func(int num) { num++; return num; }
- 함수의 매개변수를 값으로 전달(call by value)하는 경우, 해당 코드는 스레드 안전임
- 값으로 전달된 매개변수도 스레드 전용 리소스이고, 이 매개변수들도 스레드 자신만의 스택 영역에 저장됨
예시2) 스레드 안전 X
int func(int* num) { ++(*num); return *num; }
- 함수의 매개변수로 포인터를 전달하는 경우, 더이상 스레드 안전을 보장하지 않음
- 매개변수 포인터가 데이터 영역에 위치한 전역 변수 혹은 힙 영역을 가리키고 있을 수 있음
2.3.6 전역 변수 사용
예시1) 스레드 안전
- 사용되는 전역 변수가 처음 프로그램이 실행될 때 한 번 초기화된 후 모든 코드가 이 변수를 읽기만 할 경우 (읽기 전용 전역 변수)
int global_num = 100; // 한 번 초기화 후 이후 코드는 값을 변경하지 않음 int func() { return global_num; }
예시2) 스레드 안전 X
- 전역 변수의 값을 변경할 경우
int global_num = 100; int func() { ++global_num; return global_num; }
2.3.7 스레드 전용 저장소
__thread int global_num = 100; int func() { ++global_num; return global_num; }
- __thread 수식어가 붙은 변수는 스레드 전용 저장소에 배치되므로, func 함수는 다시 스레드 안전이 됨
- 해당 변수가 각 스레드에 복제되고, 각 스레드는 자신이 가진 복사본만 볼 수 있게 됨
2.3.8 함수 반환값
- 함수 반환 방식 2가지
- 값을 반환하는 경우 (return by value) - 스레드 안전
- 포인터를 반환하는 경우 (return by reference)
- 공유 리소스를 가리키는 포인터 반환 - 스레드 안전 X
- 전용 리소스를 가리키는 포인터 반환 - 스레드 안전
2.3.10 스레드 안전 코드는 어떻게 구현할까?
- 스레드 안전 구현은 스레드 전용 리소스와 스레드 공유 리소스를 중심으로 진행됨
- 스레드 전용/공유 리소스를 파악하여 아래의 방식을 적용해볼 수 있음
- 스레드 전용 저장소(thread local storage)
- 전역 리소스를 사용해야 하는 경우 스레드 전용 저장소로 선언할 수 있는지 확인
- 읽기 전용
- 전역 리소스를 반드시 사용해야 한다면 해당 전역 리소스를 읽기 전용으로 사용해도 되는지 확인
- 원자성 연산(atomic operation)
- 도중에 중단되지 않음
- 동기화 시 상호 배제(mutual exclusion in synchronization)
- 한 번에 하나의 스레드만 공유 리소스에 접근할 수 있도록 함
- 뮤텍스, 스핀 잠금, 세마포어 등의 방식 존재
- 스레드 전용 저장소(thread local storage)
'Study > 컴퓨터 밑바닥의 비밀' 카테고리의 다른 글
[컴퓨터 밑바닥의 비밀] 3.3 스택 영역: 함수 호출은 어떻게 구현될까? (0) 2025.03.09 [컴퓨터 밑바닥의 비밀] 2.7 블로킹과 논블로킹 (0) 2025.03.02 [컴퓨터 밑바닥의 비밀] 2.2 스레드 간 공유되는 프로세스 리소스 (0) 2025.03.01 [컴퓨터 밑바닥의 비밀] 2.1 운영체제, 프로세스, 스레드 (0) 2025.03.01 [컴퓨터 밑바닥의 비밀] 2.6 동기와 비동기 (0) 2025.03.01 - 스레드 안전을 달성하는 2가지 조건