std:: async
|
헤더 파일에 정의됨
<future>
|
||
|
template
<
class
F,
class
...
Args
>
std:: future < /* see below */ > async ( F && f, Args && ... args ) ; |
(1) | (C++11부터) |
|
template
<
class
F,
class
...
Args
>
std::
future
<
/* see below */
>
async
(
std::
launch
policy,
|
(2) | (C++11부터) |
함수 템플릿
std::async
는 함수
f
를 비동기적으로 (별도의 스레드에서, 이는 스레드 풀의 일부일 수 있음) 실행하고, 해당 함수 호출의 결과를 최종적으로 담게 될
std::future
를 반환합니다.
std::async
의 반환 타입은
std::
future
<
V
>
입니다. 여기서
V
는:
|
typename
std::
result_of
<
typename
std::
decay
<
F
>
::
type
(
|
(C++17 이전) |
|
std:: invoke_result_t < std:: decay_t < F > , std:: decay_t < Args > ... > . |
(C++17 이후) |
|
다음 조건 중 하나라도 만족되면 프로그램은 ill-formed입니다:
|
(C++20 이전) |
|
다음 중 하나라도 false 이면 프로그램은 ill-formed입니다:
|
(C++20 이후) |
std::async
호출은
동기화됩니다
f
호출과, 그리고
f
의 완료는
순서가 지정됩니다
공유 상태를 준비하기 전에.
목차 |
매개변수
| f | - | Callable 호출할 객체 |
| args | - | f 에 전달할 매개변수 |
| policy | - | 개별 비트들이 허용된 실행 방법들을 제어하는 비트마스크 값 |
반환값
std::future
이
std::async
호출로 생성된 공유 상태를 참조합니다.
실행 정책
비동기 호출
async
플래그가 설정된 경우, 즉
(
policy
&
std::
launch
::
async
)
!
=
0
이면,
std::async
는 호출합니다
|
INVOKE
(
decay-copy
(
std::
forward
<
F
>
(
f
)
)
,
|
(C++23 이전) |
|
std::
invoke
(
auto
(
std::
forward
<
F
>
(
f
)
)
,
|
(C++23 이후) |
마치 std::thread 객체로 표현되는 새로운 실행 스레드에서처럼.
|
decay-copy 호출은 현재 스레드에서 평가됩니다. |
(until C++23) |
|
auto 에 의해 생성된 값들은 현재 스레드에서 구체화됩니다 . |
(since C++23) |
함수
f
가 값을 반환하거나 예외를 던지면, 이는
std::future
를 통해 접근 가능한 공유 상태에 저장되며, 이
std::async
는 호출자에게 반환됩니다.
지연된 호출
만약
deferred
플래그가 설정된 경우(즉,
(
policy
&
std::
launch
::
deferred
)
!
=
0
), 그러면
std::async
는 저장합니다
|
decay-copy ( std:: forward < F > ( f ) ) 및 decay-copy ( std:: forward < Args > ( args ) ) ... 를 공유 상태에 저장합니다. |
(C++23 이전) |
|
auto ( std:: forward < F > ( f ) ) 및 auto ( std:: forward < Args > ( args ) ) ... 를 공유 상태에 저장합니다. |
(C++23 이후) |
지연 평가 가 수행됩니다:
-
호출자에게 반환된
std::async의 std::future 에 대한 첫 번째 비시간 대기 함수 호출은 INVOKE ( std :: move ( g ) , std :: move ( xyz ) ) 를 대기 함수를 호출한 스레드에서 평가합니다(이는 원래std::async를 호출한 스레드일 필요는 없습니다).
|
(C++23 이전) |
|
(C++23 이후) |
- 결과나 예외는 반환된 std::future 와 연관된 공유 상태에 배치되며, 그 후에야 준비 상태가 됩니다. 동일한 std::future 에 대한 모든 추가 접근은 즉시 결과를 반환합니다.
기타 정책
만약 std::launch::async 도 std::launch::deferred 도, 그리고 구현에서 정의된 정책 플래그도 policy 에 설정되지 않은 경우, 동작은 정의되지 않습니다.
정책 선택
둘 이상의 플래그가 설정된 경우, 어떤 정책이 선택될지는 구현에 따라 정의됩니다. 기본값( std::launch::async 와 std::launch::deferred 플래그가 모두 policy 에 설정된 경우)에 대해서는 표준에서 사용 가능한 동시성을 활용하고 추가 작업을 지연시키는 것을 권장(하지만 요구하지는 않음)합니다.
만약 std::launch::async 정책이 선택되면,
-
이
std::async호출에 의해 생성된 공유 상태를 공유하는 비동기 반환 객체의 대기 함수 호출은, 연결(joined)된 것처럼 관련 스레드가 완료되거나 타임아웃될 때까지 블록됩니다; 그리고 - 관련 스레드 완료는 공유 상태를 대기하는 첫 번째 함수의 성공적 반환과, 또는 공유 상태를 해제하는 마지막 함수의 반환과 동기화됩니다(synchronizes-with) (둘 중 먼저 발생하는 것에 대해).
예외
Throws
- std::bad_alloc , 내부 데이터 구조를 위한 메모리를 할당할 수 없는 경우, 또는
-
std::system_error
가 오류 조건
std::errc::resource_unavailable_try_again
과 함께 발생, 만약
policy
==
std::
launch
::
async
이고 구현이 새 스레드를 시작할 수 없는 경우.
- 만약 policy 가 std:: launch :: async | std:: launch :: deferred 이거나 추가 비트가 설정된 경우, 이 경우에는 지연된 호출 또는 구현 정의 정책으로 대체됩니다.
참고 사항
구현체는 기본 실행 정책에 추가적인 (구현 정의) 비트를 활성화함으로써
std::async
의 첫 번째 오버로드 동작을 확장할 수 있습니다.
구현 정의된 실행 정책의 예로는 동기 정책(즉시 실행,
std::async
호출 내에서)과 태스크 정책(
std::async
와 유사하지만 스레드 로컬 변수가 초기화되지 않음)이 있습니다.
만약
std::future
에서 얻어진
std::async
가 이동되지 않거나 참조에 바인딩되지 않으면,
std::future
의 소멸자는 전체 표현식의 끝에서 비동기 연산이 완료될 때까지 블록하게 되어, 결과적으로 다음과 같은 코드는 본질적으로 동기적으로 동작합니다:
std::async(std::launch::async, []{ f(); }); // 임시 객체의 소멸자가 f()의 완료를 대기함 std::async(std::launch::async, []{ g(); }); // f()가 완료될 때까지 시작되지 않음
std::future
를
std::async
호출 이외의 방법으로 획득한 경우, 해당 소멸자들은 절대 블록(block)하지 않습니다.
예제
#include <algorithm> #include <future> #include <iostream> #include <mutex> #include <numeric> #include <string> #include <vector> std::mutex m; struct X { void foo(int i, const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << ' ' << i << '\n'; } void bar(const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << '\n'; } int operator()(int i) { std::lock_guard<std::mutex> lk(m); std::cout << i << '\n'; return i + 10; } }; template<typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len / 2; auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // 기본 정책으로 (&x)->foo(42, "Hello") 호출: // "Hello 42"를 동시에 출력하거나 실행을 지연시킬 수 있음 auto a1 = std::async(&X::foo, &x, 42, "Hello"); // 지연 정책으로 x.bar("world!") 호출 // a2.get() 또는 a2.wait()이 호출될 때 "world!" 출력 auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // 비동기 정책으로 X()(43) 호출 // "43"을 동시에 출력 auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // "world!" 출력 std::cout << a3.get() << '\n'; // "53" 출력 } // 이 시점에서 a1이 완료되지 않았다면, a1의 소멸자가 여기서 "Hello 42"를 출력함
가능한 출력:
The sum is 10000 43 world! 53 Hello 42
결함 보고서
다음 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 수정된 동작 |
|---|---|---|---|
| LWG 2021 | C++11 |
지연된 경우 반환 타입이 잘못되었고
인수의 값 카테고리가 불명확함 |
반환 타입 수정 및
rvalue가 사용됨을 명확히 함 |
| LWG 2078 | C++11 |
std::system_error
예외가
policy
에
std::launch::async
이외의
다른 launch 정책이 지정된 경우에도 발생할 수 있는지 불명확함 |
policy
==
std::
launch
::
async
인 경우에만
발생할 수 있음 |
| LWG 2100 | C++11 |
시간 제한 대기 함수들이
std::launch::async
정책이
사용된 경우 시간 초과할 수 없었음 |
허용됨 |
| LWG 2120 | C++11 |
표준 또는 구현 정의 정책이 설정되지 않은
경우의 동작이 불명확했음 |
이 경우 동작은
정의되지 않음 |
| LWG 2186 | C++11 |
지연 평가에서 반환된 값과 발생한 예외를
어떻게 처리하는지 불명확했음 |
공유 상태에
저장됨 |
| LWG 2752 | C++11 |
std::async
가 내부 데이터 구조를 위한 메모리를
할당할 수 없는 경우 std::bad_alloc 를 발생시키지 않을 수 있었음 |
발생시킴 |
| LWG 3476 | C++20 |
(decay된 타입의)
F
와 인수 타입들이 직접적으로
이동 생성 가능해야 했음 |
이러한 요구사항 제거 [1] |
- ↑ 이동 생성 가능성은 이미 간접적으로 std::is_constructible_v 에 의해 요구됩니다.
참고 항목
|
(C++11)
|
비동기적으로 설정되는 값을 기다림
(클래스 템플릿) |
|
C++ documentation
for
Execution support library
|
|