packaged_task와 async의 차이점은 무엇입니까


134

C ++ 11의 스레드 모델로 작업하는 동안

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';

정확히 같은 일을하는 것 같습니다. std::async와 함께 실행하면 큰 차이가있을 수 std::launch::deferred있지만이 경우 하나가 있습니까?

이 두 접근법의 차이점은 무엇이며, 더 중요한 것은 어떤 유스 케이스에서 다른 것을 사용해야합니까?

답변:


161

실제로 방금 제공 한 예는 다음과 같이 다소 긴 기능을 사용하면 차이점을 보여줍니다.

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

패키지 된 작업

A packaged_task는 자체적으로 시작되지 않으므로 호출해야합니다.

std::packaged_task<int()> task(sleep);

auto f = task.get_future();
task(); // invoke the function

// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";

// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;

std::async

반면 std::asyncwith launch::async는 다른 스레드에서 작업을 실행하려고 시도합니다.

auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";

// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";

약점

그러나 async모든 것을 사용하기 전에 반환 된 미래에는 특별한 공유 상태가 있으므로 future::~future차단 해야 합니다.

std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks

/* output: (assuming that do_work* log their progress)
    do_work1() started;
    do_work1() stopped;
    do_work2() started;
    do_work2() stopped;
*/

따라서 실제 비동기식을 원한다면 return을 유지해야 future하거나 상황이 바뀌면 결과를 신경 쓰지 않아야합니다.

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

이에 대한 자세한 내용은 허브 셔터의 문서를 참조 async하고~future , 문제를 설명하고, 스콧 마이어의 std::futures에서이 std::async특별하지 않은 통찰력을 설명한다. 또한이 동작 은 C ++ 14 이상에서 지정 되었지만 일반적으로 C ++ 11에서도 구현되었습니다.

추가 차이점

사용 std::async하면 더 이상 특정 스레드에서 작업을 실행할 수 없으며 std::packaged_task다른 스레드로 이동할 수 있습니다.

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);

std::cout << f.get() << "\n";

또한을 packaged_task호출하기 전에 호출해야합니다 f.get(). 그렇지 않으면 미래가 준비되지 않으므로 프로그램이 정지됩니다.

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

TL; DR

사용 std::async당신이 어떤 일을 수행하고 완료 할 때 정말 상관하지 않으며, 원하는 경우 std::packaged_task다른 스레드로 이동하거나 나중에 전화를하기 위해 일을 마무리하려는 경우. 또는 기독교인 을 인용하자면 :

결국 a std::packaged_task는 구현을위한 저급 기능 일뿐 std::async입니다 (그래서 std::async와 같이 다른 저급 물건과 함께 사용하는 것보다 더 많은 일을 할 수있는 이유입니다 std::thread). 간단하게 음성 A는 std::packaged_taskA는 std::functionA를 연결 std::future하고 std::async랩과 호출 std::packaged_task(아마도 다른 스레드에서).


9
packaged_task에서 리턴 된 것은 그렇지 않은 반면, 소멸시 비동기 블록에 의해 리턴 된 미래 (get을 호출 한 것처럼)를 추가해야합니다.
John5342

22
결국 a std::packaged_task는 구현을위한 저급 기능 일뿐 std::async입니다 (그래서 std::async와 같이 다른 저급 물건과 함께 사용하는 것보다 더 많은 일을 할 수있는 이유입니다 std::thread). 간단하게 음성 A는 std::packaged_taskA는 std::functionA를 연결 std::future하고 std::async랩과 호출 std::packaged_task(아마도 다른 스레드에서).
Christian Rau

~ future () 블록에 대한 실험을하고 있습니다. 향후 객체 파괴에 대한 차단 효과를 복제 할 수 없었습니다. 모든 것이 비동기 적으로 작동했습니다. VS 2013을 사용하고 있으며 비동기를 시작할 때 std :: launch :: async를 사용했습니다. VC ++가 어떻게이 문제를 "수정"합니까?
Frank Liu

1
@ FrankLiu : N3451은 승인 된 제안으로 C ++ 14에 들어갔습니다. Herb가 Microsoft에서 작동한다는 것을 감안할 때 VS2013에서 해당 기능이 구현 된 경우 놀라지 않을 것입니다. C ++ 11 규칙을 엄격하게 따르는 컴파일러는 여전히이 동작을 보여줍니다.
Zeta

1
@Mikhail이 답변은 C ++ 14와 C ++ 17보다 우선하므로 표준은 없지만 제안 만 제안했습니다. 단락을 제거하겠습니다.
Zeta

1

패키지 된 작업과 비동기

p> 패키지 작업에는 작업[function or function object]과 미래 / 약속 쌍이 있습니다. 작업이 반환 문을 실행하면됩니다set_value(..)packaged_task의 약속.

a> 미래, 약속 및 패키지 작업을 통해 스레드에 대해 너무 걱정하지 않고 간단한 작업을 만들 수 있습니다 [스레드는 작업을 실행하기 위해 제공하는 것입니다].

그러나 사용할 스레드 수 또는 작업이 현재 스레드 또는 다른 스레드에서 가장 잘 실행되는지 여부를 고려해야합니다 async(). 이러한 결정은 새 스레드를 작성할지 또는 오래된 스레드를 재활용 할지를 결정 하는 스레드 시작 프로그램으로 처리 할 수 ​​있습니다. 하나 또는 단순히 현재 스레드에서 작업을 실행하십시오. 미래를 돌려줍니다.


0

"클래스 템플릿 std :: packaged_task는 호출 가능한 대상 (함수, 람다 식, 바인드 식 또는 다른 함수 개체)을 래핑하여 비동기식으로 호출 할 수 있도록합니다. 반환 값 또는 throw 된 예외는 액세스 할 수있는 공유 상태에 저장됩니다. std :: future 객체를 통해 "

"템플릿 함수 async는 함수를 비동기식으로 (잠재적으로 별도의 스레드에서) 실행하고 std :: future를 반환하여 결국 해당 함수 호출의 결과를 보유합니다."

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.