task-based programming

2022. 4. 21. 20:09Programming/JAVA, C++, Go, Rust

    목차
반응형

thread 기반보다 task 기반 programming을 선호하라는 의미는
    std::thread를 사용하는 thread 기반 방식 보다
    std::async를 사용하는 task 기반 방식을 사용하라는 것

 

thread 대신 async 사용 시,
    1) 세부 사항 제어에서 벗어날 수 있음
        prio. load balancing, thread 실행 불가 시 처리를 알아서 수행
    2) future를 통해 리턴을 획득 할 수 있음
        future를 통해 비동기 실행 function의 return을 받을 수 있음
        future 객체
            std::future, std::shared_future
3) async는 기본 software thread를 생성하지 않을 수 있음
        지정된 함수를 doAsyncWork의 결과가 필요한 thread에서 실행하게 할 수 있음
        (fut에 대해 get이나 wait을 하는 thread에서)

 

 

C++에서의 thread 의미
    1) hardware thread: CPU core 당 지니는 하나 이상의 thread
    2) software thread: OS thread/system thread
    3) C++ std::thread
        thread에 대한 handle로 작용
        handle의 null 가능
         - 기본생성자로 생성된 경우 (without handler)
         - 객체의 이동 후
         - join을 통해 다른 thread와 합류한 경우
         - detach를 통해 탈착된 경우 (끊어진 경우)

    software thread는 제한적 자원
        out of pool 경우 std::system_error exception이 발생
        task function이 noexcept로 선언되어도 발생

 

 

thread를 직접 잘못 사용 시의 문제
    thread가 많아지면 context switching의 overhead는 커짐
        core간 이동되는 context switching 시 overhead는 더 커짐
        TBL flush ? I/D cache flush

    최상의 software vs. hardware thread의 ratio는
        context switching 비율, software thread가 CPU cache를 얼마나 효과적으로 허용하는가에 의존

    이런 detail을 다른 thread로 넘기는 것이 async의 사용 임
        auto fut = std::async(doAsyncWrok);

 

 

thread를 직접 다루는 것이 적합한 경우
    1) Low level 제어 필요 경우
        affinity, priority등을 직접 제어해야 하는 경우 
        이는 std::thread의 handle을 통해 가능
        std::future는 이런 기능을 제공하지 않음 (더 high level 임)
    2) thread 사용량 최적화 필요 경우
computer 전체에 대한 동작 context는 개발자가 더 잘 알고 있을 가능성이 있기에
직접 제어 하는 것이 load balancing 측면에서 이점을 지닐 수 있음
    3) 자체적으로 thread API를 구현해야 하는 경우
        thread pool등을 직접 구현해서 성능을 높이고자 하는 경우 등

 

 

async의 동작

1) 기본 동작
async | deferred 임
기본 launch policy에서는 f가 비동기적으로 실행될 수도 있고 아닐 수도 있음

auto fut = std::async(f);
이 statement가 이를 호출한 thread에서 실행되는지, 아니면
동시 실행(get이나 wait에서 실행)인지 예측 불가임

auto fut = std::async(f);

fut이 deferred 값을 지닌 stack variant일 수도 있음
이때 

while (fut.wait_for(100ms)) {  <- 이 동작은 무한 loop
...
}

defer check가 필요
if (fut.wait_for(0s) == std::future_status::deferred) {

} else {
while (fut.wait_for(100ms) != std::future_status::ready) {
...
}
}

 

2) std::launch::async
다른 thread에서 비동기로 반드시 동작 시켜야 하는 경우
std::async로 하면 동일 thread에서 동작 시켜 반응성 문제를 야기할 수 있음
 -> 역시 GUI thread의 반응성 문제가 발생 가능
어느 thread가 반응성이 좋아야 하는 것인지 알 수 없기 때문

이때 std::launch::async를 사용
이는 현재와 다른 thread에서 수행되는 것을 강제함

최신 scheduler는 전역 thread pool을 사용해 over subscription을 막음
work-stealing 알고리즘을 이용해 load를 hardware core로 효과적으로 분배

std::thread를 사용하면 
thread 고갈, 과다구독, load balancing등을 직접 처리해야 함
다른 프로세스에서 실행되는 thread들에 대해서도 잘 맞물리게 신경써야 함

단, thread 고갈 문제 발생 가능

 

3) std::launch::deferred
future의 get, wait 호출 시부터 동기적으로 실행됨

 

 

반응형

'Programming > JAVA, C++, Go, Rust' 카테고리의 다른 글

C++ set 사용법  (0) 2022.10.08
C++ file path (파일 경로) 획득 방법  (0) 2022.10.04
Smart pointers  (0) 2022.04.21
Effective Modern C++  (0) 2022.04.21
Universal reference  (0) 2022.04.16