1 minute read

멀티 쓰레드

  • 쓰레드들은 주소 공간을 공유하기 때문에 동일한 값에 접근할 수 있다.
  • 쓰레드는 문맥 교환(context switch)을 통해 두 개이상의 쓰레드가 하나의 프로세서에서 실행하게 할 수 있다. 프로세스가 PCB를 사용하듯이 쓰레드는 `쓰레드 제어 블럭(thread control block, TCB)를 사용한다.
  • 쓰레드간의 문맥 교환은 주소 공간을 그대로 사용한다.
  • 각 쓰레드가 독립적으로 실행되며 쓰레드가 실행하기 위해 여러 루틴들을 호출할 수 있다.
  • 주소 공간에는 하나의 스택이 아니라 쓰레드마다 스택이 할당되어 있다.

Screen Shot 2023-02-03 at 10 17 23 PM

쓰레드 생성

pthread_t p1, p2;
rc = pthread_create(&p1, NULL, mythread, "A");
assert(rc &=& 0);
rc = pthread_create(&p2, NULL, mythread, "B");
assert(rc &=& 0);

쓰레드 실행 추적

Screen Shot 2023-02-03 at 10 34 08 PM

Screen Shot 2023-02-03 at 10 34 19 PM

  • 도표에서 나타내는 실행순서가 유일한 실행 가능 순서가 아니다.
  • 쓰레드 1이 쓰레드 2보다 먼저 생성된 경우에도, 만약 스케줄러가 쓰레드2를 먼저 실행하면 B가 먼저 출력될 수 있다. 즉, 먼저 생성되었다고 먼저 실행될 것이라는 가정을 할 어떤 이유가 없다.
  • 쓰레드의 생성에서 실행할 명령어들을 갖고 있는 새로운 쓰레드가 생성되고, 생성된 쓰레드는 호출자와는 별개로 실행된다.
  • 쓰레드 생성 함수가 리턴되기 전에 쓰레드가 실행될 수도 있고, 그보다 이후에 실행될 수도 있다.

데이터 공유

  • 여러 쓰레드가 다음 루틴을 실행한다고 가정해보자.
    for (int i = 0; i < 1e7; ++i)
      counter = counter + 1;
    
  • 쓰레드 갯수 * 루프 횟수만큼 counter가 증가하지 않는다.
  • 이를 이해하기 위해선, 컴파일러가 생성한 코드의 실행순서를 이해해야 한다.
mov 0x8049alc, %eax
add $0xl, %eax
mov %eax, 0x8049alc
  • 작동과정
    • 1. mov 명령어가 명시한 메모리 주소 값을 읽어들이고, eax 레지스터에 넣는다.
    • 2. (0x1)을 eax 레지스터의 값에 더하는 연산을 한다.
    • 3. eax에 저장되어 있는 값을 메모리의 원래 주소에 다시 저장한다.
  • 문제 상황

Screen Shot 2023-02-03 at 10 34 46 PM

1. 임계 영역(critical section)

  • 보통 변수나 자료 구조와 같은 공유 자원을 접근 하는 코드의 일부분을 말한다.

2. 경쟁 조건(race condition)

  • 멀티 쓰레드가 거의 동시에 임계 영역을 실행하려고 할 때 발생하며 공유 자료 구조를 모두가 갱신하려고 시도한다면 의도하지 않은 결과를 만든다.

3. 비결정적(indeterminate)

  • 프로그램은 하나 또는 그 이상의 경쟁 조건을 포함하여 그 실행 결과가 각 쓰레드가 실행된 시점에 의존하기 때문에, 프로그램의 결과가 실행할때마다 다르다.

    4. 상호 배제(mutual exclusion)

  • 이와 같은 문제들을 회피하려면 상호 배제(mutual exclusion)라는 기법의 일종을 사용하여 하나의 쓰레드만이 임계 영역에 진입할 수 있도록 한다.

원자성

  • 하드웨어가 원자성을 보장해주면 명령어 수행 도중에 인터럽트가 발생하지 않는다.
  • 원자적이라는 말은 “하나의 단위”라는 말을 뜻하며 “전부 아니면 전무”로 이해될 수 있다.