Blog Full Notice
back to main page
Thread, mutex, and raii(c++) 네이버 블로그
motivation: 네이버 블로그
#thread, mutex, and RAII(C++) : 네이버 블로그
mutex, thread 훑어보기 + RAII 개념 알아보기
나중에 mutex, thread, condition variable에 대해 공부하고 정리해서 올릴 것이다.
밑의 코드를 보자.
이 코드를 실행시키면 multithreading 한 것이 수행 시간이 더 짧다.
mutex (C++11 이상)
2개 이상의 서로 다른 thread가 같은 전역변수를 접근하고 수정 하는 상황을 race condition이라고 한다. 이 race condition이 있으면 보호해야 하는 부분을 critical section/region이라고 한다. mutex는 race condition을 피하기 위해 만들어졌고, std::mutex m1; m1.lock(), m1.unlock()을 사용해 race condition을 피할 수 있다.
어쨋든 race condition을 피하기 위해서, multithreading에서 쓸 함수에서 어떤 전역변수를 동시에 사용하게 되는 부분을 lock()과 unlock()으로 감싸준다. 그렇게 되면 서로 다른 thread에서 다른 일은 동시에 처리할 수 있는 반면, lock()과 unlock()으로 감싸진 부분은 만약 thread1이 m1.lock()을 실행했다면, thread1에서 m1.lock()이 m1.unlock()으로 풀려야 thread2가 m1.lock()을 수행할 수 있게 된다. 만약 thread1에서 m1.unlock()을 안해준다면 thread2의 critical region은 영원히 접근될 수 없는 것이다.
mutex의 늘린말은 mutual exclusion이라고 한다.
thread (C++11 이상)
multithreading을 통해 parallelism을 구현하기 위해 thread를 사용한다. thread는 OS가 배정해준다.
thread를 사용하는 방법은 1. function pointer 2. lambda function 3. functor (function object) 4. member function, 5. static member function 이 있다.
1. function pointer로 사용하는 방법은, std::thread t1(함수 이름, 함수에 들어가야하는 인수1, 인수2,...); 형식으로 사용할 수 있다. 4. member function을 사용하는 방법은 class Base 안에 public 함수 run(int x)가 있다고 하면, Base obj; std::thread t1(&Base::run, &obj, 10); 형식으로, 함수의 주소, class 객체의 주소, 파라미터 순으로 입력한다.
std::thread.join()은 thread가 끝날때까지 기다리는 것이다. std::thread t1; ...; t1.join()을 하면 그 다음 line은 thread t1이 끝나야 수행되는 것이다. 여기서 주의해야 할 점은 만약 multiple thread를 동시에 만든다면, 무엇이 먼저 시작할지 모른다. 그러나 join은 t1.join(); t2.join(); 을 하면 t1이 끝나기를 기다리고 t2가 끝나기를 기다린다.
특히 double join은 run-time으로 프로그램을 terminate 시킨다. 따라서 if(t1.joinable()) t1.join();처럼 joinable() 함수를 통해 join을 실행시켜주는 것이 좋다.
join은 "wait for this thread to complete"인 반면 detach는 "detach this thread form this parent(main) thread"이다. t1.detach()하면 이 thread를 기다리고 싶지 않다. 이 thread가 실행되는지 관심이 없다.
따라서 만약 어떤 thread를 detach했는데 main 함수가 return하게 되었다면, thread가 모두 실행되지 않고 프로그램이 끝난다. 이때 detached thread의 execution은 suspended 된다고 한다. 즉 detach의 경우 부모 thread가 return되서 없어지면 자식 thread는 실행에 상관없이 다 중단된다.
또한 join() 또는 detach()는 모든 thread에 대해서 반드시 실행되어야 한다. 그 이유는 join() 또는 detach()가 되지 않으면 thread object가 아직 joinable한지 확인해 보고, joinable하다면 thread object의 destructor가 terminate 시키기 때문이다.
RAII
resource aquisition is initialization의 줄임말이다.
resource의 성질은 1. 사용되기 전에 습득해야 하는 것이고, 2. 보통 자원이 limited된 것이다. resource의 예로 heap memory(dynamic allocation), files, mutexes 등이 있다.
그렇다면 RAII의 결과는, resource lifetime에 대해 더이상 생각할 필요가 없고, object lifetime에 대해서만 신경써면 된다. 따라서 new, delete, free, explicit하게 mutex를 lock/unlock하지 않는다.
왜 new, delete를 절대 쓰지 않아도 되는가? 왜 object lifetime만 신경써도 되는가? RAII class는 constructor에서 resource aquire를 하고, destructor에서 resource release를 해주기 때문이다. 대표적인 RAII class로는 std::vector, std::string, std::unique_ptr, std::shared_ptr, std::lock_gaurd 등이 있다. 이런 것들을 사용하면 프로그램이 안정적이게 되는 것이다. 수동으로 lock/unlock을 안해도 되는 것이다.
댓글남기기