전체 글 38

7. Event

앞선 글에서는 락을 획득하는 2가지 방법을 알아보았다. 스핀락(내 차례가 올 때까지 무한정 대기)과 Sleep을 이용한 방법(락 획득 실패 시 타임슬라이스 반환)이다.이번 글에서는 이벤트를 활용한 방법을 알아볼 것이다. 앞선 글과 같이 화장실에 비유해 본다면 이벤트를 활용한 방법은 다른 누군가에게 '화장실이 비어있으면 나에게 알려줘'라고 부탁하는 것이다.#include #include #include mutex m;queue q;void Producer(){ while (true) { { unique_lock lock(m); q.push(10); } this_thread::sleep_for(100ms); }}void Consumer(){ while (true) { unique..

6. Sleep

스핀락은 어떠한 스레드가 락을 오래 잡고 있을 경우 다른 스레드들이 while문에서 오래동안 대기해야 한다.자신이 이미 락을 획득하는데 실패했음에도 while문을 무한반복하며 할당받은 시간을 다 채우는 것이다.이때 cpu의 낭비가 발생하는데 이러한 상황에서는 스레드가 반복적으로 계속 검사를 하여 타임슬라이스를 다 채우기보단 할당받은 타임슬라이스를 반환하고 대기를 하는 것이 효율적이다.this_thread::sleep_for(chrono::milliseconds(100));this_thread::sleep_for(100ms);위의 함수가 하나의 예시이다. 인자로 시간을 건내주며 100 밀리세컨드 동안은 스케줄링 되지 않고 대기(sleep)를 한다는 것이다. 100ms라고 사용해도 무방하다.this_thre..

5. SpinLock

SpinLock이란 스레드가 공유자원을 활용하기 위해 무한루프를 빙빙돌며(Spin) 자원의 상태를 확인하는 것이다.자원이 unlock 상태가 될 때까지 그냥 무작정 기다리는 것이다.#include #include #include class SpinLock{public: void lock() { while (_locked) { } _locked = true; } void unlock() { _locked = false; }private: bool _locked = false;};int sum = 0;SpinLock spinLock;void Add(){ for (int i = 0; i guard(spinLock); sum++; }}void Sub(){ for (int i = 0; i guard..

4. DeadLock

#include #include int main(){ mutex m; m.lock()}위와 같이 lock을 한 후 unlock을 하지 않게 되면 DeadLock에 걸려 무한 대기상태가 될 수 있다. 해결방법 중 하나는 lock_guard를 활용하는 것이다.int main(){ mutex m; lock_guard guard(m);}위와 같이 사용하게 되면 guard객체가 생성되면서 lock이 걸리고 소멸되면서 자동으로 unlock이 된다.반복문이나 함수에서 사용하게 되면 lock과 unlock을 알아서 해주는 것이다. 물론 lock_guard가 모든 DeadLock을 해결해주는 것은 아니다. 아래 한 가지 예시가 있다.AccountManager는 로그인을 위해 User의 정보를 필요로 하고 User..

3. Lock

#include vector v;void Push(){ for (int i = 0; i 벡터에 1만번 push하는 코드이다.위와 같이 코드를 작성하면 출력값이 20000이 될 것 같지만 예상과 달리 오류가 난다. 지금까지 우리가 STL에서 사용하던 자료구조 등은 멀티스레드 환경에서 작동하지 않는다고 생각하는게 편하다. 다시 본문으로 돌아와서 벡터의 경우 동적 배열로 용량이 가득 차면 더 큰 캐퍼시티를 할당받고 기존의 데이터를 복사한 후 원래 메모리의 데이터를 삭제한다.여기서 문제가 발생하게 된다. 캐퍼시티를 늘리고 복사하고 삭제하는 과정에서 멀티스레드의 경우는 이러한 행위가 끝날 때까지 기다려주지 않는다.그렇기 때문에 데이터를 2번 삭제하게 된다던가 하는 문제가 생길 수 있는 것이다.그렇다면 reserv..

2. Atomic

#include int sum = 0;void Add(){ for(int i = 0; i100만번 값을 더하고 빼는 함수를 2가지 방법으로 실행했다.함수를 호출하여 실행할 경우 0이 된다는 사실은 너무나도 당연하다.근데 스레드로 실행할 경우 sum은 항상 다른 값을 가진다.공유데이터(힙 영역, 데이터 영역)를 다룰 때 이와 같은 문제가 발생할 수 있다.여기서 sum은 전역 변수로 데이터 영역에 있어 두 스레드가 경합을 통해 접근하게 된다. sum++ 또는 sum--의 경우 코드상에서 1줄로 보이지만 어셈블리어를 확인해보면 아래와 같다.1줄이었던 코드가 컴파일이 되며 어셈블리어로 변환이 될 때 3줄이 되는 것을 볼 수 있다.이 때 어떤 스레드가 sum++을 실행하기 위해 메모리에서 데이터를 가져오고 연산을..

1. 스레드 생성

cout 위와 같은 작업은 단순한 출력이 아니라 생각보다 무거운 작업이다.유저 레벨에서 간단히 할 수 있는 덧셈, 뺄셈이 아니라 모니터에 직접 출력하는 작업은 OS 커널에 요청을 하여 운영체제가 작업을 수행하는 것이다. (System Call) 스레드 생성 또한 System Call로 운영체제에 요청을 보내야 한다. 이는 운영체제마다 API 함수가 다르므로 작업 환경을 고려해야 한다.#include std::CreateThread()이렇게 윈도우 API를 활용하게 될 경우 이후 리눅스로 넘어갈 경우 수정사항이 굉장이 많아진다.물론 서버의 경우 그럴 일은 거의 없다.#include 그래도 위와 같이 모든 환경에서 작동할 수 있는 공용 라이브러리를 사용하는 것이 좋다. -스레드 생성#include void ..