프로그래밍/Parallel Programming

병렬 프로그래밍 Parallel Programming - when_all, when_any

nanze 2021. 12. 18. 23:51
반응형

이전 정리글은

2021.12.17 - [프로그래밍/Parallel Programming] - 병렬 프로그래밍 Parallel Programming - task

 

저번 정리에 이어 다시 적는다. 저번 장에서는 task 들의 순서를 결정하기 위해서 task 클래스의 then 함수를 사용하였고 이번에는 다른 방법을 사용해 보고자 한다. 

 다른 방법은 우선 예제 코드를 보자. 보통 이쪽 ? 계열 사람들은 글보단 코드가 친숙할 것이라 생각한다. :0 나만 해당할 수도 있다.

#include <ppltasks.h>
#include <iostream>

int _tmain()
{
    auto taskone = concurrency::create_task(
        [](){
            wcout << L"task one." << endl;
            return concurrency::create_task(
               []()-> int {
                   wcout << L"task another." << endl;
                   return 1;
               }
            );
        }
    );
    
    concurrency::taskthree = taskone.then(
        [](int n){
            //taskone 의 another task 가 int를 반환하는 것을 확인.!
            wcout << L"task three. number : " << n << endl;
        }
    );
    taskthree.wait();
    return 0;
}

 위 코드를 보면 taskone 의 함수 실행 부분에서 반환을 다른 task를 생성하면서 끝나는 것을 알 수 있다. task another 는 taskone 이 종료되는 시점에 실행되는 것이다. taskthree 는 ?? 당연히 then 함수를 이용하였기 때문에 task another가 끝난 후 실행될 수 있다. task 의 순서는 다음과 같다. taskone >>> taskanother >>> taskthree !!!! 

 여기서 기존의 then 을 사용할 때와 다르게 확인해야 할 부분이 있다.! 바로 task 내부에서 생성되어지는 task의 함수는 인자로 이전 task 의 반환 타입  형태를 갖지 못하고 항상 void 라는 것이다. ~! 참 어렵다 ㅜㅜ   그리고 taskthree 는 then 함수를 사용해서 task를 연결하고 있는데 task another 가 마지막으로 반환되어 int 타입을 가진 인자의 함수가 되어야 하며 이 경우에는 concurrency::task<int> 와 같은 task 형태의 인자를 가질 수는 없다. 이유는 taskone 때문일 것이다. 모든 내부 구조까지 확인하기에는 쿨럭 .....

 다음은 나열된 여러 task 들의 순서를 제어할 수 있는 부분들에 대해서 정리해보자. 

 

when_all

 우선 여러 task 들을 제어하기 위해서는 한 자료 구조에 담겨져 있어야 가능할 것이다. 음.. 배열에 담겨있어야 편하겄지.

 concurrency::when_all 함수는 배열에 담긴 task 들의 모든 작업이 끝나길 대기해주는 즉 모든 작업의 join 역할을 한다고 생각하면 좋을 것 같다. when_all 함수는 반환 값이 존재하는데 그것은 task<vector<T>> 인 것이다. 이것을 보면 유추할 수 있는 것이 배열에 존재하는 task 들의 반환 타입은 모두 T 로 동일해야 한다는 것이다. 각 task 들의 반환 T 타입의 값들이 vector 에 담기는 것이다. 

#include <ppltasks.h>
#include <iostream>
#include <array>
#include <numeric>

using namespace std;

int _tmain()
{
    array< concurrency::task<int>, 3> arTask = {
        concurrency::create_task( []()-> int { return 1;}), 
        concurrency::create_task( []()-> int { return 2;}),
        concurrency::create_task( []()-> int { return 3;})
    };
    
    auto taskAfterWhenAll = concurrency::when_all(begin(arTask), end(arTask)).then(
        [](vector<int> vecResTasks){
            wcout << L"total : " << accumulate(begin(vecResTasks), end(vecResTasks), 0) << endl;
        }
    );
    
    taskAfterWhenAll.wait();
    
    return 0;
}

 위 코드에서 taskAfterWhenAll 작업은 모든 작업들이 완료된 것을 기다리고 각 작업들이 반환한 int 값을 합치고 있다. ppl에서는 when_all 함수와 같은 행동을 task 들의 bool 표현식으로도 표현할 수 있게 한다. 참 이 세상에는 비상한 머리를 가진 사람들이 너무 많다. 그것에 감사한다.!

#include <ppltasks.h>
#include <iostream>
#include <array>
#include <numeric>

using namespace std;

int _tmain()
{
    array< concurrency::task<int>, 3> arTask = {
        concurrency::create_task( []()-> int { return 1;}), 
        concurrency::create_task( []()-> int { return 2;}),
        concurrency::create_task( []()-> int { return 3;})
    };
    
    auto taskWhenAll = arTask[0] && arTask[1] && arTask[2];
    
    auto taskAfterWhenAll = taskWhenAll.then(
        [](vector<int> vecResTasks){
            wcout << L"total : " << accumulate(begin(vecResTasks), end(vecResTasks), 0) << endl;
        }
    );
    
    taskAfterWhenAll.wait();
    
    return 0;
}

 위 코드는 이전 코드와 동일하게 동작한다. :)

 

when_any

 이 함수는 무엇일까? when_all 은 모든 작업이 종료되기를 대기했다. 그렇다면 이것은~! 그렇다. task 들 중 어느 작업 하나만 끝나도 반환된다. 반환 값은 무엇일까? 바로 가장 먼저 종료된 task 의 반환 값과 자료구조상 인덱스를 가지고 있는 task< pair<T, size_t> 를 반환한다.!! 다 필요없다. 코드를 보자.!!!

#include <ppltasks.h>
#include <iostream>
#include <array>
#include <numeric>

using namespace std;

int _tmain()
{
    array< concurrency::task<int>, 3> arTask = {
        concurrency::create_task( []()-> int { return 1;}), 
        concurrency::create_task( []()-> int { return 2;}),
        concurrency::create_task( []()-> int { return 3;})
    };
    
    auto taskAfterWhenAny = concurrency::when_any(begin(arTask), end(arTask)).then(
        [](pair<int, size_t> res){
            wcout << L"task return value : " << res.first << "task index : " res.second << endl;
        }
    );
    
    taskAfterWhenAny.wait();
    
    return 0;
}

  위 코드를 보면 가장 먼저 반환한 task의 반환 값과 배열 내에서의 위치를 보여준다. 또한 when_all 과 마찬가지로 bool 표현식도 지원한다.~!

#include <ppltasks.h>
#include <iostream>
#include <array>
#include <numeric>

using namespace std;

int _tmain()
{
    array< concurrency::task<int>, 3> arTask = {
        concurrency::create_task( []()-> int { return 1;}), 
        concurrency::create_task( []()-> int { return 2;}),
        concurrency::create_task( []()-> int { return 3;})
    };
    
    auto taskWhenAny = arTask[0] || arTask[1] || arTask[2];
    auto taskAfterWhenAny = taskWhenAny.then(
        [](pair<int, size_t> res){
            wcout << L"task return value : " << res.first << "task index : " res.second << endl;
        }
    );
    
    taskAfterWhenAny.wait();
    
    return 0;
}

 킁. 몇 자 정리도 못 했는데 벌써부터 피곤하다. 참 세상에는 공부할 것이 너무 많다. 행복한 것인지 불행한 것인지 모르겠네 .

 

다음 정리글은

2021.12.20 - [프로그래밍/Parallel Programming] - 병렬 프로그래밍 Parallel Programming - task_group

 

 

 

 

반응형