std :: promise 란 무엇입니까?


384

나는 C ++ 11 개의와 매우 잘 알고 std::thread, std::asyncstd::future구성 요소 (예 : 볼 이 대답 ) 솔직하다.

그러나 나는 std::promise그것이 무엇인지, 무엇을하는지, 어떤 상황에서 가장 잘 사용되는지 파악할 수 없습니다 . 표준 문서 자체에는 클래스 개요 이외의 많은 정보가 포함되어 있지 않으며 just :: thread 도 포함하지 않습니다 .

누군가 std::promise가 필요한 상황 과 가장 관용적 인 해결책에 대한 간략한 간결한 예를 들어 줄 수 있습니까?


2
여기에 코드가 있습니다 : en.cppreference.com/w/cpp/thread/future
chris

58
정말, 정말 짧은 버전은 : std::promise어디 std::future의에서 온. 약속std::future 된 값을 검색 할 수 있습니다. 미래에 전화 할 때 , 약속 을 부름으로써 값을 설정하는 소유자가 될 때까지 기다립니다 . 값이 설정되기 전에 약속이 파기 된 다음 해당 약속과 관련된 미래 를 요청 하면 값이 약속되었으므로 예외 가 발생하지만 약속을 잡는 것은 불가능합니다. get()std::promiseset_valueget()std::broken_promise
James McNellis

14
난 당신이 할 수있는 경우에, 원하는 / 그 추천 한 번 봐 걸릴 액션 C ++ 동시성 에 의해 앤서니 윌리엄스
dribeas - 데이비드 로드리게스를

32
@KerrekSB std::broken_promise는 표준 라이브러리에서 가장 명명 된 식별자입니다. 그리고 없습니다 std::atomic_future.
Cubbi

3
Downvoter, 당신의 이의 제기를 설명 할까?
Kerrek SB 2014

답변:


182

[futures.state]의 단어에서 a std::future비동기 리턴 오브젝트 ( "공유 상태에서 결과를 읽는 오브젝트")이고 a std::promise비동기 제공자 ( "공유 상태에 결과를 제공하는 오브젝트")입니다. 약속은 결과 를 설정 한 것이므로 관련 미래에서 얻을 수 있습니다.

비동기 공급자는 처음에 미래가 참조하는 공유 상태를 만드는 것입니다. std::promise한 유형의 비동기 제공자이고 std::packaged_task다른 유형이며 내부 세부 사항 std::async도 다른 유형입니다. 이들 각각은 공유 상태를 생성하고 std::future해당 상태를 공유하고 상태를 준비 할 수 있습니다.

std::async비동기 결과 개체를 제공하고 내부적으로 비동기 공급자를 만들고 작업 완료시 공유 상태를 준비하는 고급 편의 유틸리티입니다. std::packaged_task(또는 std::binda std::promise)와 a로 에뮬레이션 할 수 std::thread있지만 더 안전하고 사용하기 쉽습니다 std::async.

std::promise비동기 결과를 미래에 전달하려는 경우에 대비하여 조금 낮은 수준이지만 결과를 준비하는 코드를에 전달하기에 적합한 단일 함수로 래핑 할 수 없습니다 std::async. 예를 들어, 여러 개의 배열 promise과 관련된 배열을 future가지고 있고 여러 계산을 수행하고 각 약속에 대한 결과를 설정하는 단일 스레드가있을 수 있습니다. async하나의 결과 만 반환하고, 여러 개를 반환하려면 async여러 번 호출해야하므로 리소스가 낭비 될 수 있습니다.


10
자원을 낭비 할 수 있습니까? 해당 코드를 병렬화 할 수없는 경우 올바르지 않을 수 있습니다.
강아지

"비동기 리턴"과 "공유 상태의 결과 읽기"는 대부분 직교이므로 첫 번째 문장이 약간 혼동됩니다. 국가의 분담이 미래와 약속 사이에 있다고 말하는가? 그렇다면 처음부터 명시 적으로 말하십시오.
einpoklum

@einpoklum 왜 마지막 단어 전에 "비동기 리턴 객체"를 읽지 않았습니까? 나는 표준의 용어를 인용하고있다. A future는 공유 상태를 통해 비동기 적으로 반환 된 결과를 읽는 객체비동기 반환 객체 의 구체적인 예입니다 . A promise비동기 공급자 의 구체적인 예입니다. 이 값은 공유 상태에 값을 쓰는 객체이며 비동기 적으로 읽을 수 있습니다. 나는 내가 쓴 것을 의미했다.
Jonathan Wakely

496

나는 지금 상황을 조금 더 잘 이해하고 (여기서 답변으로 인해 소액은 없습니다!), 나는 내 자신의 작은 글씨를 추가한다고 생각했습니다.


C ++ 11에는 서로 관련이 있지만 서로 다른 두 가지 개념이 있습니다. 비동기 계산 (다른 곳에서 호출되는 함수)과 동시 실행 ( 스레드 , 동시에 작동 하는 스레드 ). 이 두 가지는 다소 직교하는 개념입니다. 비동기 계산은 다른 기능 호출과는 달리 스레드는 실행 컨텍스트입니다. 쓰레드는 그 자체로는 유용하지만,이 논의의 목적 상 쓰레드를 구현 세부 사항으로 취급 할 것이다.


비동기 계산을위한 추상화 계층이 있습니다. 예를 들어, 몇 가지 인수를 취하는 함수가 있다고 가정하십시오.

int foo(double, char, bool);

먼저, std::future<T>type 유형의 미래 값을 나타내는 템플릿 이 있습니다 T. 멤버 함수를 통해 값을 검색 할 수 있으며 get()결과를 기다리면 프로그램을 효과적으로 동기화합니다. 또는 향후 지원을 wait_for()통해 결과가 이미 사용 가능한지 여부를 조사 할 수 있습니다. 선물은 일반적인 수익 유형에 대한 비동기 드롭 인 대체로 생각해야합니다. 예제 함수의 경우을 기대합니다 std::future<int>.

이제 계층에서 최상위 레벨에서 최저 레벨까지 :

  1. std::async: 비동기 계산을 수행하는 가장 편리하고 간단한 방법은 async함수 템플릿을 사용하여 일치하는 미래를 즉시 반환하는 것입니다.

    auto fut = std::async(foo, 1.5, 'x', false);  // is a std::future<int>

    우리는 세부 사항을 거의 제어하지 않습니다. 특히, 함수가 동시에, 직렬로 get()또는 다른 흑 마법 으로 실행되는지조차 알지 못합니다 . 그러나 필요한 경우 결과를 쉽게 얻을 수 있습니다.

    auto res = fut.get();  // is an int
  2. 우리는 이제 와 같은 것을 구현 하는 방법을 고려할 수 async있지만 우리가 통제 하는 방식으로 . 예를 들어, 우리는 함수가 별도의 스레드에서 실행되도록 주장 할 수 있습니다. 우리는 std::thread클래스를 통해 별도의 스레드를 제공 할 수 있다는 것을 이미 알고 있습니다 .

    다음 단계의 추상화는 정확히 다음을 수행 std::packaged_task합니다. 이것은 함수를 감싸고 함수 반환 값의 미래를 제공하는 템플릿이지만 객체 자체는 호출 가능하며 호출은 사용자의 재량에 따릅니다. 다음과 같이 설정할 수 있습니다.

    std::packaged_task<int(double, char, bool)> tsk(foo);
    
    auto fut = tsk.get_future();    // is a std::future<int>

    우리가 작업을 호출하고 호출이 완료되면 미래는 준비됩니다. 별도의 스레드에 이상적인 작업입니다. 우리 는 작업을 스레드로 이동시켜야 합니다.

    std::thread thr(std::move(tsk), 1.5, 'x', false);

    스레드가 즉시 실행되기 시작합니다. 우리는 detach그것을 join스코프의 끝에서 또는 언제든 가질 수 있습니다 (예 : scoped_thread표준 라이브러리에 있어야하는 Anthony Williams의 래퍼 사용 ). 그러나 사용에 대한 자세한 내용은 std::thread여기서 우리와 관련이 없습니다. thr결국에는 연결 하거나 분리하십시오 . 중요한 것은 함수 호출이 완료 될 때마다 결과가 준비된다는 것입니다.

    auto res = fut.get();  // as before
  3. 이제 가장 낮은 수준에 도달했습니다. 패키지 된 작업을 어떻게 구현 합니까? 이것이 시작되는 곳 std::promise입니다. 약속은 미래와 소통하기위한 빌딩 블록입니다. 주요 단계는 다음과 같습니다.

    • 호출 스레드가 약속합니다.

    • 호출 스레드는 약속에서 미래를 얻습니다.

    • 함수 인수와 함께 약속은 별도의 스레드로 이동됩니다.

    • 새 스레드는 기능을 실행하고 약속을 이행합니다.

    • 원래 스레드는 결과를 검색합니다.

    예를 들어, 다음과 같은 "패키지 된 작업"이 있습니다

    template <typename> class my_task;
    
    template <typename R, typename ...Args>
    class my_task<R(Args...)>
    {
        std::function<R(Args...)> fn;
        std::promise<R> pr;             // the promise of the result
    public:
        template <typename ...Ts>
        explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
    
        template <typename ...Ts>
        void operator()(Ts &&... ts)
        {
            pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
        }
    
        std::future<R> get_future() { return pr.get_future(); }
    
        // disable copy, default move
    };

    이 템플릿의 사용법은 기본적으로와 동일합니다 std::packaged_task. 전체 작업을 이동하면 약속을 이동하는 것으로 간주됩니다. 보다 임시적인 상황에서는 promise 객체를 새 스레드로 명시 적으로 이동하여 스레드 함수의 함수 인수로 만들 수 있지만 위와 같은 작업 래퍼는보다 유연하고 덜 침입적인 솔루션처럼 보입니다.


예외 만들기

약속은 예외와 밀접한 관련이 있습니다. 약속의 인터페이스만으로는 상태를 완전히 전달하기에 충분하지 않으므로 약속의 조작이 의미가 없을 때마다 예외가 발생합니다. 모든 예외는 std::future_error에서 파생 된 유형 입니다 std::logic_error. 먼저 몇 가지 제약 조건에 대한 설명입니다.

  • 기본 구성 약속은 비활성입니다. 비활성 약속은 결과없이 죽을 수 있습니다.

  • 를 통해 미래를 얻으면 약속이 활성화됩니다 get_future(). 그러나 단 하나의 미래 만 얻을 수 있습니다!

  • 미래가 사용될 경우 약속은 수명이 끝나기 전에 충족 set_value()되거나 예외가 설정되어 있어야합니다 set_exception(). 만족스러운 약속은 결과없이 죽을 get()수 있으며 앞으로도 가능해집니다. 예외가있는 약속 get()은 미래 에 전화하면 저장된 예외를 제기합니다 . 약속이 가치도없고 예외도없이 죽으면 get(), 미래를 부르는 것은“깨진 약속”예외를 일으킬 것입니다.

다음은 이러한 다양한 뛰어난 동작을 보여주는 작은 테스트 시리즈입니다. 먼저, 하네스 :

#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>

int test();

int main()
{
    try
    {
        return test();
    }
    catch (std::future_error const & e)
    {
        std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
    }
    catch (std::exception const & e)
    {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unknown exception." << std::endl;
    }
}

이제 테스트를 진행하십시오.

사례 1 : 비활성 약속

int test()
{
    std::promise<int> pr;
    return 0;
}
// fine, no problems

사례 2 : 미사용 약속

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    return 0;
}
// fine, no problems; fut.get() would block indefinitely

사례 3 : 미래가 너무 많다

int test()
{
    std::promise<int> pr;
    auto fut1 = pr.get_future();
    auto fut2 = pr.get_future();  //   Error: "Future already retrieved"
    return 0;
}

사례 4 : 만족스러운 약속

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
    }

    return fut.get();
}
// Fine, returns "10".

사례 5 : 너무 많은 만족

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // Error: "Promise already satisfied"
    }

    return fut.get();
}

두 개 이상있을 경우 같은 예외가 발생 하나set_valueset_exception.

사례 6 : 예외

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
    }

    return fut.get();
}
// throws the runtime_error exception

사례 7 : 약속 약속

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
    }   // Error: "broken promise"

    return fut.get();
}

당신은 말했다 "효과적으로 결과를 기다리고하여 프로그램을 동기화하는 ...." . 여기서 "동기화"는 무엇을 의미합니까? 전체 진술은 무엇을 의미합니까? 이해할 수 없습니다. 이 사전 항목 에서 "동기화"의 의미 는 문장을 이해하는 데 도움 이되지 않습니다. "대기 중"만 "동기화"를 의미합니까? 모든 대기가 동기화됩니까? 나는 당신이 무엇을 의미하는지 부분적으로 이해한다고 생각하지만, 당신이 실제로 무엇을 의미 하는지 잘 모르겠습니다 .
Nawaz

9
std :: async의 일부에 대해 다른 스레드를 생성하거나 flag (std :: launch :: async, std :: launch :: deferred)와 함께 작동하도록 결정할 수 있음을 기억합니다.
StereoMatching

1
@FelixDombek : 퍼펙트 포워딩 등에 std::function는 많은 생성자가 있습니다. 소비자에게 노출시키지 않을 이유가 없습니다 my_task.
Kerrek SB

1
@DaveedV .: 피드백 주셔서 감사합니다! 그렇습니다. 테스트 사례 7입니다. 가치 나 예외를 설정하지 않고 약속을 파기 get()하면 미래를 부르면 예외가 발생합니다. "파괴되기 전에"를 추가하여이를 명확히 할 것입니다. 충분히 명확한 지 알려주십시오.
Kerrek SB

3
마지막으로 got()future온 스레드 지원 라이브러리를 grokking의 promise당신의 놀라운 설명!
sunny moon

33

Bartosz Milewski 는 훌륭한 글쓰기를 제공합니다.

C ++은 미래의 구현을 작은 블록으로 나눕니다.

std :: promise는 이러한 부분 중 하나입니다.

약속은 함수를 실행하는 스레드에서 함수 미래에 현금화하는 스레드로 반환 값 (또는 예외)을 전달하는 수단입니다.

...

미래는 약속 채널의 수신단 주위에 구성된 동기화 객체입니다.

따라서 미래를 원한다면 비동기 처리의 결과를 얻는 데 사용하겠다는 약속으로 끝납니다.

이 페이지의 예는 다음과 같습니다.

promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException

4
스레드 생성자의 약속을 보면 마침내 페니가 떨어졌습니다. Bartosz의 기사는 아마도 가장 크지는 않지만 요소가 어떻게 묶여 있는지 설명합니다. 감사.
Kerrek SB

28

대략적인 근사치 std::promise에서 다른 끝으로 간주 할 수 있습니다 std::future(이것은 거짓 이지만 설명을 위해 마치 마치 생각할 수 있습니다). 통신 채널의 소비자 std::future측은 공유 상태에서 데이텀을 소비하기 위해 a 를 사용하는 반면 생산자 스레드는 a std::promise를 사용 하여 공유 상태에 기록합니다.


12
@ KerrekSB : std::async개념적으로 (표준에 의해 요구되지 않음)을 생성 std::promise하고 스레드 풀로 푸시 하는 함수로 이해 할 수 있습니다 (정렬, 스레드 풀, 새로운 스레드 일 수 있음 ...) std::future발신자 와 관련된 . 클라이언트 쪽에서는를 기다리고 std::future다른 쪽의 스레드는 결과를 계산하여에 저장합니다 std::promise. 참고 : 표준이 필요 공유 상태std::future아니지만의 존재를 std::promise이 특정 사용 케이스를.
David Rodríguez-dribeas

6
@ KerrekSB : 스레드를 std::future호출하지 않으며 실제 통신 버퍼 인 공유 상태에join 대한 포인터가 있습니다. 공유 상태는 동기화 메커니즘을 (아마도이 +가 가 될 때까지 발신자를 잠 그려면 성취 스레드의 실행이 모든 직교한다. 많은 구현에 당신이이 찾을 수있는 다음 결합 된 새로운 스레드에 의해 실행되지 않지만, . 오히려 그 수명이 프로그램이 끝날 때까지 확장 스레드 풀에 의해std::functionstd::condition_variablestd::promisestd::async
데이비드 로드리게스 - dribeas

1
@ DavidRodríguez-dribeas : 의견의 정보를 답변으로 편집하십시오.
Marc Mutz-mmutz 2016 년

2
@JonathanWakely : 새 스레드에서 실행되어야하는 것이 아니라 새로 작성된 스레드에서 실행되는 것처럼 비동기식 으로 실행되어야한다는 의미 입니다. 가장 큰 장점은 std::async런타임 라이브러리가 생성 할 스레드 수와 관련하여 올바른 결정을 내릴 수 있으며 대부분의 경우 스레드 풀을 사용하는 런타임을 예상한다는 것입니다. 현재 VS2012는 후드에서 스레드 풀을 사용하며 as-if 규칙을 위반하지 않습니다 . 이 특정 as-if에 대해 충족해야 할 보장은 거의 없습니다 .
David Rodríguez-dribeas

1
스레드 로컬을 다시 초기화해야하지만 as-if 규칙은 무엇이든 허용합니다 (따라서 이탤릭체로 "as if"를 넣는 이유)
Jonathan Wakely

11

std::promise비동기 함수에서 정보가 리턴 될 채널 또는 경로입니다. std::future호출 메커니즘은 반환 값 std::promise이 준비 될 때까지 호출자가 대기하게합니다 (값이 함수 내부에 설정됨을 의미).


8

비동기 처리에는 실제로 3 가지 핵심 엔터티가 있습니다. C ++ 11은 현재 그중 2 개에 중점을두고 있습니다.

일부 로직을 비동기식으로 실행하는 데 필요한 핵심 사항은 다음과 같습니다.

  1. '어딘가'를 실행 하는 작업 (논리 일부 functor 객체로 패키지 됨).
  2. 실제 프로세싱 노드 - 스레드, 프로세스 등 그들이 그것이 제공하는 경우 이러한 펑 실행. 기본 작업자 스레드 풀이이를 수행하는 방법에 대한 좋은 아이디어는 "명령"디자인 패턴을 참조하십시오.
  3. 결과 핸들 : 누군가가 그 결과를 필요로하고 그들을 위해 그것을 얻을 것이다 객체를 필요로한다. OOP 및 기타 이유로 인해이 핸들의 API에서 대기 또는 동기화를 수행해야합니다.

C ++ 11은 내가 (1)에서 말하는 std::promise것과 (3)에서 말하는 것을 부릅니다 std::future. std::thread(2)에게 공개적으로 제공된 유일한 것입니다. 실제 프로그램은 스레드 및 메모리 리소스를 관리해야하며 대부분의 작은 작업마다 스레드를 생성 및 제거하는 대신 스레드 풀에서 작업을 실행하기를 원하기 때문에 불행한 일입니다 (거의 항상 자체적으로 불필요한 성능 저하를 유발하고 쉽게 리소스를 생성 할 수 있음) 더 나쁜 기아).

Herb Sutter와 C ++ 11의 뇌 신뢰에있는 다른 사람들에 따르면, std::executorJava와 같은 것을 추가하려는 잠정적 인 계획이 스레드 풀과 (2)의 논리적으로 유사한 설정의 기초가 될 것입니다. 어쩌면 우리는 C ++ 2014에서 그것을 볼 수 있지만 내 베팅은 C ++ 17과 비슷합니다 (그리고 그들이 표준을 어기면 하나님이 우리를 도와주십시오).


7

A std::promise는 약속 / 미래 쌍의 끝점으로 작성되며 std::future( get_future()방법을 사용하여 std :: promise에서 작성 됨 ) 다른 끝점입니다. 이것은 하나의 스레드가 메시지를 통해 다른 스레드에 데이터를 제공 할 때 두 스레드가 동기화 할 수있는 방법을 제공하는 간단한 방법입니다.

하나의 스레드가 데이터 제공 약속을 작성하고 다른 스레드가 향후 약속을 수집한다고 생각할 수 있습니다. 이 메커니즘은 한 번만 사용할 수 있습니다.

약속 / 미래기구는 사용하는 실에서 하나 개의 방향 set_value()(A)의 방법을 std::promise용도에 실 get()(A)의이 std::future데이터를 수신하기 위해. get()미래 의 메소드가 두 번 이상 호출 되면 예외가 생성됩니다 .

와 스레드가있는 경우 std::promise사용하지 않은 set_value()두 번째 스레드를 호출 할 때 다음 약속을 이행 get()의이 std::future약속을 수집하는 약속이와 첫 번째 스레드에 의해 충족 될 때까지, 두 번째 스레드는 대기 상태가됩니다 std::promise가 사용하는 경우에 set_value()방법을 데이터를 전송합니다.

제안 된 기술 사양 N4663 프로그래밍 언어-Coroutines 용 C ++ 확장 및 Visual Studio 2017 C ++ 컴파일러 지원으로 Coroutine 기능 co_await을 사용 std::future하고 std::async작성할 수 있습니다. 의 토론과 예를 참조 https://stackoverflow.com/a/50753040/1466970 의 사용에 대해 설명 한 부분으로 가지고 std::future와를 co_await.

간단한 Visual Studio 2013 Windows 콘솔 응용 프로그램 인 다음 예제 코드는 몇 가지 C ++ 11 동시성 클래스 / 템플릿 및 기타 기능을 사용하는 방법을 보여줍니다. 그것은 잘 작동하는 약속 / 미래, 일부 작업을 수행하고 중지하는 자율 스레드, 더 많은 동기 동작이 필요하고 약속 / 미래 쌍이 작동하지 않는 여러 알림의 필요성으로 인해 사용하는 방법을 보여줍니다.

이 예제에 대한 한 가지 참고 사항은 다양한 장소에서 추가 된 지연입니다. 이 지연은 콘솔을 사용하여 인쇄 된 다양한 메시지 std::cout가 명확하고 여러 스레드의 텍스트가 섞이지 않도록하기 위해 추가되었습니다.

의 첫번째 부분은 main()세 개의 부가적인 스레드를 만들고 사용 std::promise하고 std::future스레드간에 데이터를 전송할. 흥미로운 점은 메인 스레드가 스레드 T2를 시작하는 위치이며,이 스레드는 메인 스레드에서 데이터를 기다린 후 무언가를 수행 한 다음 세 번째 스레드 인 T3으로 데이터를 전송합니다. 메인 스레드.

main()작성의 두 번째 부분 은 두 개의 스레드와 큐 세트를 작성하여 기본 스레드에서 두 개의 작성된 스레드 각각으로 여러 메시지를 허용합니다. 우리는 사용할 수 없습니다 std::promisestd::future약속 / 미래 듀오가 하나의 샷입니다 반복적으로 사용 할 수 없기 때문하십시오.

이 클래스의 소스 Sync_queue는 Stroustrup의 The C ++ Programming Language : 4th Edition에 있습니다.

// cpp_threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

#include <list>    // std::list which we use to build a message queue on.

static std::atomic<int> kount(1);       // this variable is used to provide an identifier for each thread started.

//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
//   The C++ Programming Language, 4th Edition by Bjarne Stroustrup
//   copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
    void  put(const Ttype &val);
    void  get(Ttype &val);

private:
    std::mutex mtx;                   // mutex used to synchronize queue access
    std::condition_variable cond;     // used for notifications when things are added to queue
    std::list <Ttype> q;              // list that is used as a message queue
};

template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
    std::lock_guard <std::mutex> lck(mtx);
    q.push_back(val);
    cond.notify_one();
}

template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
    std::unique_lock<std::mutex> lck(mtx);
    cond.wait(lck, [this]{return  !q.empty(); });
    val = q.front();
    q.pop_front();
}
//------------------------------------------------


// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();   // wait for the promise attached to the future
    std::cout << "  func " << myId << " future " << ll << std::endl;
}

// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();     // wait for the promise attached to the future

    auto promiseValue = ll * 100;   // create the value to provide as promised to the next thread in the chain
    pp.set_value(promiseValue);
    std::cout << "  func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}

// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
    int myId = std::atomic_fetch_add(&kount, 1);

    int ll;
    q.get(ll);    // wait on a notification and when we get it, processes it.
    while (ll > 0) {
        std::cout << "  func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
        for (int i = iBegin; i < iEnd; i++) {
            pInts[i] = ll + i;
        }
        q.get(ll);  // we finished this job so now wait for the next one.
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::milliseconds myDur(1000);

    // create our various promise and future objects which we are going to use to synchronise our threads
    // create our three threads which are going to do some simple things.
    std::cout << "MAIN #1 - create our threads." << std::endl;

    // thread T1 is going to wait on a promised int
    std::promise<int> intPromiseT1;
    std::thread t1(func, std::ref(intPromiseT1));

    // thread T2 is going to wait on a promised int and then provide a promised int to thread T3
    std::promise<int> intPromiseT2;
    std::promise<int> intPromiseT3;

    std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));

    // thread T3 is going to wait on a promised int and then provide a promised int to thread Main
    std::promise<int> intPromiseMain;
    std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
    intPromiseT1.set_value(22);

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
    std::this_thread::sleep_for(myDur);
    intPromiseT2.set_value(1001);
    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;

    std::future<int> intFutureMain(intPromiseMain.get_future());
    auto t3Promised = intFutureMain.get();
    std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;

    t1.join();
    t2.join();
    t3.join();

    int iArray[100];

    Sync_queue<int> q1;    // notification queue for messages to thread t11
    Sync_queue<int> q2;    // notification queue for messages to thread t12

    std::thread t11(func3, std::ref(q1), 0, 5, iArray);     // start thread t11 with its queue and section of the array
    std::this_thread::sleep_for(myDur);
    std::thread t12(func3, std::ref(q2), 10, 15, iArray);   // start thread t12 with its queue and section of the array
    std::this_thread::sleep_for(myDur);

    // send a series of jobs to our threads by sending notification to each thread's queue.
    for (int i = 0; i < 5; i++) {
        std::cout << "MAIN #11 Loop to do array " << i << std::endl;
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q1.put(i + 100);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q2.put(i + 1000);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
    }

    // close down the job threads so that we can quit.
    q1.put(-1);    // indicate we are done with agreed upon out of range data value
    q2.put(-1);    // indicate we are done with agreed upon out of range data value

    t11.join();
    t12.join();
    return 0;
}

이 간단한 응용 프로그램은 다음과 같은 출력을 만듭니다.

MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
  func 1 future 22
MAIN #2.2 - provide the value for promise #2
  func2 2 promised 100100 ll was 1001
  func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
  func3 4 start loop base 100 0 to 5
  func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
  func3 4 start loop base 101 0 to 5
  func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
  func3 4 start loop base 102 0 to 5
  func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
  func3 4 start loop base 103 0 to 5
  func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
  func3 4 start loop base 104 0 to 5
  func3 5 start loop base 1004 10 to 15

1

약속은 전선의 다른 쪽 끝입니다.

future의해 계산되는 값을 검색해야한다고 상상해보십시오 async. 그러나이 같은 스레드에서 계산하고, 당신도 "지금"스레드를 생성하지 않는 싶지 않아 - 어쩌면 소프트웨어가 풀에서 스레드를 선택하도록 설계되었습니다, 당신이 모르는, 그래서 누가 것이다 결국 che 계산을 수행하십시오.

자,이 (아직 알려지지 않은) 스레드 / 클래스 / 엔터티로 무엇을 전달합니까? future결과 이므로을 전달하지 않습니다 . 당신은 무언가 전달하려는 연결된 받는 사람을 future그 나타냅니다 와이어의 다른 쪽 끝을 당신은 단지를 조회 할 수 있도록, future누가 것이다 실제로 계산 / 쓰기 뭔가에 대해 전혀 지식.

이것은입니다 promise. 에 연결된 핸들future 입니다. (가) 경우 futureA는 스피커 와 함께 get()하면 어떤 소리가 나올 때까지 듣기 시작하면이 promiseA는 마이크 ; 하지만 그냥 마이크, 그것은이다 당신이 보유하고있는 스피커에 하나의 선으로 연결 마이크. 상대방이 누구인지 알 수 있지만 알 필요는 없습니다. 그냥주고 상대방이 무언가를 말할 때까지 기다리십시오.


0

http://www.cplusplus.com/reference/future/promise/

한 문장 설명 : furture :: get ()은 promse :: set_value ()를 영원히 기다립니다.

void print_int(std::future<int>& fut) {
    int x = fut.get(); // future would wait prom.set_value forever
    std::cout << "value: " << x << '\n';
}

int main()
{
    std::promise<int> prom;                      // create promise

    std::future<int> fut = prom.get_future();    // engagement with future

    std::thread th1(print_int, std::ref(fut));  // send future to new thread

    prom.set_value(10);                         // fulfill promise
                                                 // (synchronizes with getting the future)
    th1.join();
    return 0;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.