[시스템프로그래밍] 조건 변수와 시그널, Reader-Writer problem에 관하여

2023. 12. 5. 14:15·Study/시스템프로그래밍

Busy waiting

  • while( x != y );

Non-busy waiting 해결책

  • Mutex를 잠그기
    • 조건 x == y를 테스트
      • 참이면 mutex를 잠금 해제하고 루프를 종료
      • 거짓이면 스레드를 일시 중단하고 mutex를 잠금 해제

Mutex는 데이터에 대한 스레드 액세스를 제어하여 동기화를 구현하지만

  • 조건 변수는 실제 데이터 값에 기반하여 스레드를 동기화할 수 있게 함.

만약 조건 변수가 없다면,

  • 프로그래머는 계속해서 조건이 충족되었는지 확인하기 위해 스레드를 폴링(가능하면 임계 구역에서)
  • 이는 스레드가 이 활동에 계속해서 바쁘게 차지되어 매우 자원 소모적일 수 있음.
  • 조건 변수는 폴링 없이 동일한 목표를 달성하기 위한 방법
  • 조건 변수는 항상 Mutex 락과 함께 사용

조건 변수 : 임의의 조건이 참이 될 때까지 대기하는 프로세스 큐와 관련된 새로운 데이터 유형

 

pthread_cond_wait

  • 조건 변수와 뮤텍스를 매개변수로 사용
  • 호출된 스레드를 원자적으로 중단시키고 뮤텍스를 잠금 해제합니다.
    • 스레드가 알림을 받으면 뮤텍스가 다시 획득
    • 뮤텍스를 소유한 스레드만 호출해야

 

pthread_cond_signal

  • 조건 변수를 매개변수로 사용하고 해당 대기 큐에서 대기 중인 스레드 중 하나를 깨움
  • 스레드를 조건 변수 큐에서 뮤텍스 큐로 이동시키는 효과

조건 x == y를 사용하여 조건 변수 v와 뮤텍스 m으로 대기

pthread_mutex_lock(&m);
while( x != y )
	pthread_cond_wait(&v, &m);
/* modify x or y if necessary */
pthread_mutex_unlock(&m)

대기 중인 스레드에게 x가 증가했음을 알리는 다른 스레드

pthread_mutex_lock(&m);
x++;
pthread_cond_signal(&v);
pthread_mutex_unlock(&m);

 

조건 변수 생성

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

 

pthread_cond_t

  • 조건 변수를 나타내는 변수 형식
  • 사용 전에 항상 초기화되어야 함

정적 변수의 초기화

  • 단순히 'PTHREAD_COND_INITIALIZER'를 할당

동적으로 할당된 변수의 초기화

  • pthread_cond_init 호출
  • 속성 객체에 대한 'attr' 매개변수, 기본값은 NULL

성공 시 0, 실패 시 0이 아닌 오류 코드 반환

이미 초기화된 조건 변수의 초기화 →동작이 정의되어 있지 않음

 

조건 변수 파괴(destroying condition variables)

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);

성공 시 0, 실패 시 0이 아닌 오류 코드 반환

밑의 두 사례는 정의되지 않음

  • 스레드가 파괴된 조건 변수를 참조하는 경우
  • 다른 스레드가 차단된 상태에서 조건 변수를 파괴하려고 하는 경우

 

조건 변수에서 Signaling

#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

pthread_cond_signal

  • 'cond'가 가리키는 조건 변수에 대기 중인 스레드 중 최소한 하나를 깨움

pthread_cond_broadcast

  • 'cond'가 가리키는 조건 변수에 대기 중인 모든 스레드를 깨웁니다.

성공 시 0, 실패 시 0이 아닌 오류 코드 return

 

스레드에서 신호 전달

하나의 프로세스에서 모든 스레드는 프로세스 신호 핸들러를 공유 각 스레드는 자신만의 신호 마스크(signal mask)를 가지고 있습니다.

비동기식(Asynchronous)

  • 해당 신호가 차단되지 않은 어떤 스레드에 전달

동기식(Synchronous) • 해당 신호를 발생시킨 스레드에 전달됩니다.

지시식(Directed) • 식별된 스레드에 전달 (pthread_kill)

pthread_kill()

#include <signal.h>
#include <pthread.h>
int pthread_kill(pthread_t thread, int sig);

'sig' 신호 번호가 생성되어 'thread'로 지정된 스레드에 전달되도록 요청

성공 시 0, 실패 시 0이 아닌 오류 코드 return

pthread_mask()

#include <pthread.h>
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

스레드의 신호 마스크를 검사하거나 설정

 

‘how' 매개변수

  • SIG_SETMASK • 스레드의 신호 마스크를 'set'으로 대체
  • SIG_BLOCK • 'set'에 있는 추가 신호를 차단
  • SIG_UNBLOCK • 현재 스레드의 신호 마스크에서 차단 중인 'set'의 신호를 제거

'oset' 매개변수

  • NULL이 아닌 경우, 함수는 *oset을 스레드의 이전 신호 마스크로 설정합니다.

성공 시 0, 실패 시 0이 아닌 오류 코드 return

멀티스레드 프로세스에서 신호 처리를 다룰 때

특정 스레드를 신호 처리에 할당 , 주 스레드는 모든 신호를 차단

할당된 스레드 생성 →해당 신호에 대해 sigwait()를 실행

(pthread_sigmask를 사용하여 신호를 차단 해제할 수 있음)

 

Reader-Writer Problem

리소스가 두 가지 유형의 액세스(읽기 및 쓰기)를 허용하는 상황에서

  • 쓰기는 배타적으로 허용되어야 하고
  • 읽기는 공유될 수 있음

두 가지 일반적인 전략

  • 강력한 reader 동기화(Strong reader synchronization)
    • reader에게 우선권을 부여하여 현재 작성 중인 작성자가 없는 한 리더에게 액세스를 부여
  • 강력한 writer 동기화
    • writer에게 우선권을 부여하여 모든 대기 중이거나 활성인 writer가 완료될 때까지 reader를 지연

POSIX는 읽기-쓰기 잠금을 제공 – 작성자가 잠금에서 차단되어 있는 경우 reader가 잠금을 획득할지 여부는 구현에 따라 다름

read-write locks 초기화 - pthread_rwlock_init()

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, 
const pthread_rwlockattr_t *restrict attr);

읽기-쓰기 잠금을 나타내는 변수 형식

  • 사용 전에 항상 초기화되어야 함
  • 'attr'
    • 읽기-쓰기 잠금 속성 객체를 가리키는 포인터

성공 시 0, 실패 시 0이 아닌 오류 코드 return

이미 초기화된 읽기-쓰기 잠금의 초기화 → 동작이 정의되어 있지 않음

Locking/unlocking

#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

rdlock/tryrdlock

  • 스레드가 읽기용으로 읽기-쓰기 잠금을 획득할 수 있게 한다.

wrlock/trywrlock

  • 스레드가 쓰기용으로 읽기-쓰기 잠금을 획득할 수 있게 한다.

unlock

  • 잠금을 해제한다.

성공 시 0, 실패 시 0이 아닌 오류 return

tryrdlock 및 trywrlock은 잠금이 이미 보유되어 있기 때문에 잠금을 획득할 수 없는 경우 EBUSY를 반환

tryrdlock 함수는 **rdlock**과 비슷하지만, 다른 스레드에서 쓰기용으로 해당 잠금을 보유하고 있을 때 대기하지 않는다.

  • 만약 다른 스레드에서 쓰기용으로 잠금을 보유하고 있다면, **tryrdlock**은 즉시 실패하고 오류 코드를 반환

스레드가 wrlock으로 이미 획득한 잠금에 대해 rdlock을 호출하는 경우 →데드락이 발생할 수도

strerror()

  • thread-safe하지 않은 몇 가지 함수 중 하나
  • strerror()이 동시에 사용된다면 뮤텍스 잠금으로 보호해야 함
  • perror()나 strerror() 둘 다 async-signal 안전하지 않음

해결책

  • 동기화를 래퍼로 캡슐화(encapsulate the synchronization in a wrapper)
  • perror_r()과 strerror_r()은 둘 다 thread-safe하고 async-signal 안전
    • strerror이 사용하는 정적 버퍼에 대한 동시 액세스를 방지하기 위해 뮤텍스 사용
    • perror()도 동일한 뮤텍스로 보호하여 동시 실행을 방지
    • 뮤텍스를 잠그기 전에 모든 시그널을 차단
      • 그렇지 않으면 시그널 핸들러 내부에서 이러한 함수 중 하나를 호출하면 데드락이 발생할 수 있음

 

데드락과 다른 까다로운 문제

동기화 구조를 사용하는 프로그램은 POSIX 기본 표준의 구현에서 감지되지 않을 수 있는 데드락 가능성이 있다.

스레드가 이미 보유한 뮤텍스에 대해 pthread_mutex_lock을 실행하는 경우

  • pthread_mutex_lock은 실패하고 EDEADLK를 반환할 수 있지만, 표준(standard)는 이 함수가 그렇게 해야 한다고 요구하지 않는다.

락을 보유한 스레드가 오류를 만나는 경우

  • 반환하기 전에 락을 해제해야 한다.

우선순위가 있는 스레드 (우선순위 역전)

  • 낮은 우선순위 스레드가 뮤텍스를 보유한 상태에서 긴 시간동안 실행되는 중간 우선순위의 통신 스레드가 가끔씩 선점되어 높은 우선순위의 스레드가 긴 시간 동안 지연되는 상황이 발생할 수 있다.
저작자표시 비영리 변경금지 (새창열림)

'Study > 시스템프로그래밍' 카테고리의 다른 글

[시스템프로그래밍] 임계구역과 세마포어에 관하여  (1) 2023.12.05
[시스템프로그래밍] Mutex에 관하여  (4) 2023.12.05
[시스템프로그래밍] 스레드  (0) 2023.12.04
[시스템프로그래밍] 시간과 타이머  (0) 2023.12.03
[시스템프로그래밍] Signals  (1) 2023.12.03
'Study/시스템프로그래밍' 카테고리의 다른 글
  • [시스템프로그래밍] 임계구역과 세마포어에 관하여
  • [시스템프로그래밍] Mutex에 관하여
  • [시스템프로그래밍] 스레드
  • [시스템프로그래밍] 시간과 타이머
퀵차분
퀵차분
Web Developer 🥐
  • 퀵차분
    QC's Devlog
    퀵차분
  • 전체
    오늘
    어제
    • 분류 전체보기 (178)
      • Frontend (31)
      • Fedify (4)
      • Study (42)
        • NestJS (2)
        • Node.js (3)
        • Modern JS Deep Dive (13)
        • SQL (1)
        • Network (1)
        • 프롬프트 엔지니어링 (4)
        • 인공지능 (9)
        • 시스템프로그래밍 (11)
        • 선형대수학 (1)
      • Intern (4)
      • KUIT (21)
      • Algorithm (48)
        • Baekjoon(C++) (26)
        • Programmers(JavaScript) (22)
      • 우아한테크코스(프리코스) (4)
      • Project (10)
        • crohasang_page (3)
        • PROlog (4)
        • Nomadcoder (2)
      • 생각 (4)
      • Event (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    알고리즘
    음악추천
    티스토리챌린지
    javascript
    fedify
    react
    타입스크립트
    시스템프로그래밍
    next.js
    백준
    리액트
    프로그래머스 자바스크립트
    프로그래머스
    HTML
    오블완
    KUIT
    프론트엔드
    자바스크립트
    인공지능
    typescript
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
퀵차분
[시스템프로그래밍] 조건 변수와 시그널, Reader-Writer problem에 관하여
상단으로

티스토리툴바