ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [컴퓨터 밑바닥의 비밀] 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)
        • 한 번에 하나의 스레드만 공유 리소스에 접근할 수 있도록 함
        • 뮤텍스, 스핀 잠금, 세마포어 등의 방식 존재

    댓글

Designed by Tistory.