Skip to content

바람직한 스레드 디자인과 구현을 위한 가이드라인입니다.

Notifications You must be signed in to change notification settings

jaguarcode/Multithread

Repository files navigation

Multithread

Multithread with C++17

바람직한 스레드 디자인과 구현을 위한 가이드라인

  • 표준 라이브러리는 방대한 종류의 라이브러리를 제공한다. C++17부터는 그중 60개 이상이 병렬 실행을 지원한다. 멀티스레드 코드를 직접 구현하기보다는 가능하면 표준 라이브러리의 병렬 알고리즘을 사용하는 것이 좋다. 알고리즘이 병렬 모드로 실행하도록 하는 옵션은 ...(18장) 이다.

  • 어플리케이션을 종료하기 전에 반드시 조인해야 할 thread 객체가 하나도 남지 않게 한다. 모든 thread 객체에 대해 join()이나 detach()를 호출했는지 확인한다. 조인할 예정인 thread 소멸자는 std::terminate()를 호출하게 된다. 그러면 모든 스레드와 어플리케이션이 갑자기 종료된다.

  • 동기화 메커니즘이 없는 동기화 방식이 최고다. 멀리스레드 프로그래밍을 할 때 공유 데이터를 다루는 스레드가 그 데이터를 읽기만 하고 쓰지 않게 또는 다른 스레드가 읽지 않은 부분만 쓰도록 구성하면 코드를 훨씬 쉽게 구현할 수 있다. 그러면 동기화 메커니즘을 따로 구현할 필요 없으며, 데이터 경쟁이나 데드락도 발생 하지 않는다.

  • 가능하다면 싱글 스레드 소유권 패턴을 적용한다. 다시 말해 데이터 블록을 한 번에 한 스레드만 소유하게 만든다. 데이터를 소유한다(데이터의 소유권을 갖는다)는 말은 다른 스레드가 그 데이터를 읽거나 쓸 수 없다는 뜻이다. 스레드가 데이터에 대한 작업을 마치면 그 데이터에 대한 소유권을 다른 스레드로 넘길 수 있다. 그러면 그 스레드만 데이터 소유권을 갖게 돼 동기화 메커니즘이 필요 없다.

  • 아토믹 타입과 아토믹 연산을 최대한 활용한다. 아토믹 타입과 아토믹 연산을 사용하면 데이터 경쟁과 데드락이 발생하기 않게 만들기 쉽다. 동기화 작업을 알아서 처리해주기 때문이다. 아토믹 타입과 연산을 제공하지 않는 환경에서 데이터를 공유해야 한다면 상호 배제와 같은 동기화 메커니즘을 이용하여 여러 스레드의 읽기 및 쓰기 연산을 동기화 시켜야 한다.

  • 락을 거는 기간은 짧을수록 좋다. 공유 데이터를 락으로 보호할 때는 최대한 빨리 해제한다. 한 스레드가 락을 걸고 있으면 그 락을 기다리는 다른 스레드가 블록돼 전체 성능이 떨어질 수 있다.

  • 여러 개의 락을 걸 때는 직접 구현하지 말고 std::lock()이나 std::try_lock()을 사용한다. 여러 스레드가 락을 여러 개 걸어야 한다면 반드시 모든 스레드를 똑같은 순서로 걸어야 한다. 그렇지 않으면 데드락이 발생할 수 있다. 이렇게 여러 개의 락을 걸 때는 제네릭 함수인 std::lock()이나 std::try_lock()을 사용한다.

  • RAII 락 객체를 사용한다. 락이 제때 자동으로 해제되도록 lock_guard, unique_lock, shared_lock, scope_lock과 같은 RAII 클래스를 사용한다.

  • 멀티스레드를 지원하는 프로파일러를 활용한다. 그러면 멀티스레드로 구현한 어플리케이션에서 발생하는 성능 저하 지점뿐만 아니라 현재 생성된 스레드가 시스템의 처리량을 최대로 활용하고 있는지 쉽게 알아낼 수 있다. 멀티스레드를 지원하는 프로파일러의 예로 마이크로소프트웨어 비주얼 스튜디오에서 제공하는 프로파일러가 있다.

  • 멀티스레드를 지원하는 디버거를 활용한다. 대부분의 디버거는 멀티스레드 어플리케이션을 디버깅하는 데 필요한 최소 기능을 제공한다. 적어도 어플리케이션에서 현재 구동하고 있는 스레드 목록을 조회하거나, 그중 원하는 스레드의 콜스택을 조회하는 기능을 갖추고 있어야 한다. 그러면 각각의 스레드가 현재 실행되는 현황을 정확히 볼 수 있기 때문에 데드락 검사와 같은 작업을 수행할 수 있다.

  • 스레드가 많을 때는 필요할 때마다 생성했다가 삭제하지 말고 스레드 풀을 이용한다. 동적으로 생성했다 삭제하는 스레드 수가 많을수록 성능 저하 폭이 크다. 이럴 때는 스레드 풀을 이용하여 기존에 생성된 스레드를 최대한 재사용하는 것이 좋다.

  • 하이레벨 멀티스레딩 라이브러리를 활용한다. 현재 시점에서 C++ 표준은 멀티스레드 코드를 작성하는데 아주 기본적인 기능만 제공한다. 이러한 기능을 제대로 활용하기란 쉽지 않다. 따라서 스레드 관련 기능을 직접 구현하지 말고, 인텔의 TBB/ MS의 PLL과 같은 하이레벨 관점으로 멀티스레딩을 지원하는 라이브러리를 활용하는 것이 좋다. 멀티스레드 프로그램을 에러 없이 정확히 동작하게 만드는 것은 쉽지 않다. 또한 직접 구현한 것이 생각보다 유용하지 않을 수도 있다.

About

바람직한 스레드 디자인과 구현을 위한 가이드라인입니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages