Post

스레드 동기화1

스레드 동기화의 필요성

다수의 스레드가 동시에 공유 데이터에 쓰기를 접근하는 경우

  • 두 스레드가 동시에 공유 데이터를 읽는 경우는 문제가 없다.
  • 한 스레드는 쓰고 다른 스레드는 읽는 경우, 읽고 쓰는 순서에 따라 읽는 값이 달라질 수 있지만 공유데이터의 훼손은 없다.
  • 문제가 되는건 두 스레드가 동시에 공유 데이터를 쓰는 경우이다. 이 때 공유 데이터가 훼손될 가능성이 있다.

스레드 동기화(thread synchronization)란?

  • 공유데이터에 대한 다수의 스레드가 동시에 접근할 때, 한 스레드가 공유 데이터를 배타적, 독점적으로 접근하도록 순서화해 공유 데이터가 훼손되지 않게 하는 기법이다.

멀티스레드의 경쟁 상황

  • 매우 자주 발생하며, 주로 커널 코드에서 발생한다.(커널에 공유 데이터가 많기 때문이다.)
  • 다중 코어에서 특히 유의해야 한다.

스레드동기화사례

예시코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <pthread.h>
int sum = 0; // 두 스레드가 공유하는 변수
void* worker(void* arg) { // 스레드 코드
for(int i=0; i<1000000; i++) {
    sum = sum + 10;
}
}
int main() {
    char *name[] = {"황기태", "이찬수"};
    pthread_t tid[2]; // 2개의 스레드 ID를 담을 배열
    pthread_attr_t attr[2]; // 2개의 스레드 정보를 담을 배열
    pthread_attr_init(&attr[0]); // 디폴트 속성으로 초기화
    pthread_attr_init(&attr[1]); // 디폴트 속성으로 초기화
    pthread_create(&tid[0], &attr[0], worker, name[0]); // 스레드 생성
    pthread_create(&tid[1], &attr[1], worker, name[1]); // 스레드 생성
    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    printf("sum = %d\n", sum); // 두 스레드 종료 후 sum 출력
    return 0;
}


공유 데이터 접근 문제의 해결책

임계구역(critical section): 공유 데이터에 접근하는 프로그램 코드들

상호배제(mutual exclusion): 임계구역을 오직 한 스레드만 배타적 독점적으로 사용하도록 하는 기술



상호배제

상호 배제를 포함하는 전형적인 프로그램

  1. 일반 코드(non critical code)
    • 공유 데이터를 액세스하지 않는 코드
  2. 임계구역 진입 코드(entry code)
    • 임계구역에 진입하기 전 필요한 코드 블록, 현재 임계구역을 실행중인 스레드가 있는지 검사하고, 있다면 다른 스레드가 들어오지 못하도록 조치하고, 없다면 진입이 가능해질 때까지 대기한다.
  3. 임계구역 코드(critical code)

  4. 임계구역 진출 코드(exid code)
    • 임계구역을 마칠 때 필요한 코드 블록
    • 대기중인 스레드가 임계구역에 진입할 수 있도록, 진입 코드에서 취한 조치 해제

상호 배제 구현

  • 구현 목표는 임계구역에 오직 1개의 스레드만 진입하도록 하는 것이다.
  • 구현 방법은 두 가지가 존재한다.
    • 소프트웨어적 방법 : Perersons’s 알고리즘 -> 알고리즘 수준에서 제시된 것들로 구현 시 여러 문제에 노출된다.

    • 하드웨어적 방법 : 하드웨어의 도움을 받는 방법으로 인터럽트 서비스 금지, 원자 명령 활용 등이 있다. 오늘날은 대부분 하드웨어적 방법을 사용한다.

  • 하드웨어적 방법
    • 방법1 - 인터럽트 서비스 금지
      • 인터럽트 서비스를 금지하거나 허용하는 CPU 명령을 사용한다.
    • 방법2 - 원자 명령(atomic instruction) 사용
      • 원자 명령은 CPU 명령
      • 오늘날 상호 배제 구현에 사용하는 방법이다.


방법 1: 인터럽트 서비스 금지

방법

  • entry 코드에서 인터럽트 서비스 금지 명령 실행
  • 인터럽트가 발생해도 CPU는 인터럽트 서비스 루틴을 실행하지 않는다.
  • 임계구역을 실행하는 스레드가 중단되지 않는다.

문제점

  • 모든 인터럽트가 무시되는 문제 발생
  • 멀티 코어 CPU나 다중 CPU를 가진 시스템에서 활용이 불가능하다.

단순 lock 변수 활용

  • locking/unlocking 방식으로 임계구역의 entry/exit 코드를 작성하면 상호배제가 가능할까?


방법 2: 원자명령(atomic instruction) 사용

lock 변수를 이용한 상호배제의 실패 원인

  • entry 코드에서 lock 변수 값을 읽는 명령과 lock 변수에 1을 저장하는 2개의 명령 사이에 컨텍스트 스위칭이 될 때 문제가 발생한다.

해결책 : 원자명령 도입

  • lock변수를 읽어들이는 명령, 변수에 1을 저장하는 2개의 명령을 한 번에 처리하는 원자명령 필요
  • TSL(Test and Set Lock)
    <!–

멀티 스레드 동기화 기법

멀티 스레드 동기화란?

  • 상호 배제 기반 위에, 자원을 사용하려는 여러 스레드들이 자원을 원활히 공유하도록 하는 기법
  • 동기화 프리미티브(synchronization primitives)

대표 기법

  • locks 방식 : 뮤텍스(Mutex), 스핀락(spinlock)
  • wait-signal 방식 : 세마포(semaphore) –>
This post is licensed under CC BY 4.0 by the author.