일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 공룡책
- JUCE라이브러리
- JUCE 튜토리얼
- JUCE library
- gui
- 연결리스트
- go
- OS
- LOB
- 코딩
- 리듬게임
- C++ gui
- go channel
- a tour of go
- C언어
- 운영체제
- C++
- 백준
- 알고리즘
- Docker
- JUCE
- tour of go
- 프로그래밍
- vim-go
- 자료구조
- C++ library
- c++ heap
- C++ gui 라이브러리
- Nebula
- BOJ
- Today
- Total
CafeM0ca
[OS] Thread 입문 본문
서론
쓰레드의 코드로 표현하는 방법은 구글링을 통해 많은 예제들을 볼 수 있다. 여기서는 좀 더 구체적인 이론들과 사용하는 방법에 대해 학습해보자.
이론
햄버거 가게에서 카운터 한명이 주문을 받고 있다. 주문을 기다리는 손님의 줄이 끝없이 보인다. 아뿔싸, 조리대에는 1명의 직원밖에 없어 손님들의 주문이 하나 둘씩 밀리기 시작한다.. - 살..려..줘
위 예시에서 햄버거 가게가 갖고 있는 문제는 2가지다.
- 카운터 한명으로는 너무 많은 손님을 커버할 수 없다.
- 조리대 직원은 밀려오는 주문에 고통받고 있다.
이를 해결할 방법은 매니저가 취할 수단은 직원을 더 고용하는 것이다. 2명의 카운터가 주문을 받는다면 주문 처리 속도는 2배가 된다. 2명의 조리대 직원이 햄버거를 제조하면 같은 시간 동안 생산량이 2배가 된다.
thread도 마찬가지다. 우리가 콘솔창에 Hello World를 출력하는 프로그램은 하나의 main thread로 구성되어 있다.
하지만 큰 프로그램은 엄청나게 많은 작업들이 존재하는데 이를 main thread 혼자서 어찌 감당할까..
그래서 우리는 각각의 Thread(일 하는 노예)에게 작업(work)을 분배(division) 하여 더 빠르게 프로그램이 동작하길 원한다.
생산자-소비자(Producer-Cunsumer)
thread는 각각의 역할을 분리하는게 효율적이다. 햄버거를 주문받아 주문목록을 추가하는 역할과 주문목록을 하나씩 없애는 소비하는 역할로 구분하면 적절하다.
간단하게 2명의 카운터가 햄버거를 주문받는 코드를 작성했다.
#include <iostream>
#include <thread>
#include <queue>
using namespace std;
typedef struct {
int order_num;
string hamberger_name;
} order_info;
void order(queue<order_info>& order_queue, string hamberger_name){
static int order_number = 0; // 현재 주문 번호
order_info order{order_number, hamberger_name};
order_queue.emplace(order); // 주문 대기열에 추가한다.
order_number++;
}
int main()
{
queue<order_info> order_queue; // 주문 목록
std::thread counter[2]; // 주문 받는 카운터
counter[0] = std::thread(order, ref(order_queue), "monster X");
counter[1] = std::thread(order, ref(order_queue), "truffle X");
counter[1].join();
counter[0].join();
while(!order_queue.empty()){
cout << order_queue.front().order_num << ": " << order_queue.front().hamberger_name << endl;
order_queue.pop();
}
return 0;
}
분명 주문은 2개인데 매 실행마다 번호가 동일하다. thread는 어느것이 먼저 실행될 지 모른다. 우리는 동시에 공유된 자원에 접근하는걸 원하지 않았다. 이처럼 둘 이상의 입력이나 실행 순서, 결과에 영향을 줄 수 있는 상태를 Race Condition이라 한다.
Data Race
Data Race는 복수의 쓰레드가 공유된 자원(예제의 order_number)에 접근할 때 발생하는 문제다.
이를 해결하기 위해서는 Mutual Exclusion(상호 배제)가 필요하다.
Mutual Exclusion(상호 배제, == mutex)
Mutual Exclusion는 thread가 공유된 자원에 먼저 접근할 때, 뒤이은 thread가 공유 자원에 접근하지 못하도록 배제하는 것이다.
공유 자원에 접근하는 구간을 critical section이라 한다.
mutex 메커니즘은 공유 자원이 있는 critical section의 entry section(입장 구역)에서 다른 thread들이 접근하지 못하도록 lock 해두고 공유자원을 사용한다. 다 사용하면 lock을 해제하고 다른 thread의 접근을 허용한다.
- 여러 사람이(복수의 thread) 방에 들어간다고 했을때 (entry section)
- 먼저 방에 들어간 사람(thread)이 방문을 잠그고 (Locking)
- 방에서 할 일을 하고 (공유 자원 사용)
- 방문을 열고 나온다. (Unlocking)
- 2~4과정을 반복
Spin Lock
Spin Lock은 busy-waiting(바쁜 대기)의 한 종류로 loop를 돌면서 화장실에 들어간 thread에게 언제 나오냐고 재촉하는 것과 같다.
읽기-쓰기(Readers-Writers)
식사하는 철학자들(Dining Philoseophers)
Referece
- Clean-Code(Robert.C.Martin
'OS' 카테고리의 다른 글
[자료실] OS관련 자료들 (0) | 2018.07.03 |
---|---|
[OS]용어집 (0) | 2017.12.04 |