future, async 는 mutex, condition variable 까지 쓰지 않고 단순한 쓰레드 처리시 유용하다
1회성 사용시에 유용
ex)파일로딩
1) async : 원하는 함수를 비동기적으로 실행
2) promise : 결과물을 promise를 통해 future 를 통해 받아줌
3) packged_task : 원하는 함수의 실행 결과를 packged_task를 통해 future 로 받아 줌
2,3은 비슷하다
비동기 != 멀티쓰레드
단지 용어 상의 차이인데 비동기는 나중에 함수를 호출하는 개념
즉 지연 호출의 개념이 있어서 완전 멀티 스레드라고 할 수는 없다
wait_for
연결된 비동기 작업이 완료되거나 지정된 _Rel_time 시간이 경과할 때까지 차단합니다.
구문
template <
class _Rep,
class _Period
>
std::future_status::future_status wait_for(
const std::chrono::duration< _Rep, _Period>& _Rel_time ) const;
매개 변수
_Rep
틱 수를 나타내는 산술 형식입니다.
_Period
틱당 경과된 시간(초)을 나타내는 std::ratio입니다.
_Rel_time
작업이 완료될 때까지 대기하는 최대 시간입니다.
반환 값
HRESULT = NO_ERROR를
- std::future_status::deferred 연결된 비동기 작업이 실행되고 있지 않으면
- std::future_status::ready 연결된 비동기 작업이 완료된 경우
- std::future_status::timeout 지정된 기간이 경과된 경우
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <future>
#include "windows.h"
using namespace std;
int64 calculate()
{
int64 sum = 0;
for (int32 i = 0; i < 100'000; ++i)
{
sum += 1;
}
return sum;
}
int main()
{
{
//비 동기 호출, calculate 가 호출
std::future<int64> future = std::async(std::launch::async, calculate);
std::future_status status = future.wait_for(1ms); //1밀리 세컨동안 대기했다가 상태를 얻어와서
//지금 스레드의 상태가 어떤 상태인지 알 수 있다
//std::future_status::ready, std::future_status::deferred, std::future_status::timeout
/*
* std::future_status::deferred 연결된 비동기 작업이 실행되고 있지 않으면
std::future_status::ready 연결된 비동기 작업이 완료된 경우
std::future_status::timeout 지정된 기간이 경과된 경우
*/
if(status == std::future_status::ready) //ready 면 일감이 완료 된상태
{
}
int64 sum = future.get(); //get 을 만나면 이때까지 스레드가 실행 안됐다면 대기했다가 결과 반환
}
return 0;
}
ready 면 일감이 완료된 상태
future.wait() == wiat_for 를 무한정 기다리는 상태
//비 동기 호출, calculate 가 호출
std::future<int64> future = std::async(std::launch::async, calculate);
//std::future_status status = future.wait_for(1ms); //1밀리 세컨동안 대기했다가 상태를 얻어와서
future.wait(); //wiat_for 를 무한정 기다리는 상태
//그래서 wait 을 했다가 결과를 호출하나 아래에서 get 을 호출하나 결과는 같다
int64 sum = future.get(); //get 을 만나면 이때까지 스레드가 실행 안됐다면 대기했다가 결과 반환
wiat_for 추가 설명
future 는 thread에서 연산된 결과 값을 전달 받기 위해 사용된다.
wait() / get()은 thread에서 return 또한 set_value() 함수가 호출될 때까지 무한정 대기한다.
wait_for()를 사용하면 일정 시간만큼 thread에서 값이 전달되기 기다리다, timout이 되면 block 상태가 해제된다.
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int main()
{
std::future<int> future = std::async(std::launch::async, [](){
std::this_thread::sleep_for(std::chrono::seconds(3));
return 8;
});
std::cout << "waiting...\n";
std::future_status status;
do {
status = future.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred) {
std::cout << "deferred\n";
} else if (status == std::future_status::timeout) {
std::cout << "timeout\n";
} else if (status == std::future_status::ready) {
std::cout << "ready!\n";
}
} while (status != std::future_status::ready);
std::cout << "result is " << future.get() << '\n';
}
Line별로 자세히 살펴보도록 하자.
std::future<int> future = std::async(std::launch::async, [](){
std::this_thread::sleep_for(std::chrono::seconds(3));
return 8;
});
std::launch::async 정책으로 async()를 수행하는데, lamda 표현식을 사용하여 thread를 생성하였다. ( 3초를 sleep하고 8을 return )
std::future_status status;
do {
status = future.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred) {
std::cout << "deferred\n";
} else if (status == std::future_status::timeout) {
std::cout << "timeout\n";
} else if (status == std::future_status::ready) {
std::cout << "ready!\n";
}
} while (status != std::future_status::ready);
이 섹션에서 핵심 코드라고 할 수 있다.
wait_for() 함수는 아래와 같은 return 값을 가진다.
wait_for()에서 second(1)을 전달하여 1초 대기하도록 선언하였다. 앞서 본 thread에서 3초 sleep 후 8을 return 하기에
do - while 1번째에는 timeout이 발생하게된다. (timeout 로그 출력)
이후 do - while 조건문에서 status가 ready가 아니므로, 다시 wait_for() 함수가 호출된다. thread는 아직 3초가 지나지 않았기에 다시 한번 timeout이 발생한다. (timeout 로그 출력)
다시 do - while 3번째에서 wait_for() 함수가 호출되어 1초를 wait하게 되는데, 이제는 thread가 3초 sleep에서 깨어나 8을 return 하게 된다.
그래서 wait_for() 함수는 ready를 리턴하게 된다.
do - while 문에서 status가 ready가 되어 do - while문을 빠져 나와 get() 함수를 통해 8을 출력하게 된다.
클래스 인스턴스와 멤버 함수 호출 시
class ABC
{
public :
int64 getalph() {
return 10;
}
};
ABC abc;
std::future<int64> future2 = std::async(std::launch::async, &ABC::getalph, abc);
auto result = future2.get();
결과는 10
std::promise 와 std::future 조합
스레드 만들고 전역변수 만들어 데이터 전달하기 보단 스레드 함수에서 데이터를
assign 하여 스래드 대기하는 쪽에서 데이터를 받아 볼 수 있는 기법
void promiseWorker(std::promise<string>&& promise)
{
promise.set_value("secret message");
}
main 함수 중....
{
//아래 코드처럼 작성하면
//promise 에서 future 를 얻어올 수 있음
//만약 promise 에 어떤 데이트를 세팅하면
//future 에서 데이터를 얻어올 수 있음
//미래에 결과물을 반환해달라는 일종의 약속(promise) 하는 것
std::promise<string> promise;
std::future<string> future = promise.get_future();
//여기 까지 설정한다음
//스레드를 만들어 소유권을 넘겨주면
//즉 std::move(promise) 를 promiseWorker 스레드에 인자로 넘겨 준다
thread t(promiseWorker, std::move(promise));
//그리고 위 코드에서
//promise.set_value("secret message");
//를 했음으로 promise 에서 이 데이터를 다음 처럼 받아 올 수 있다
string data = future.get();
std::cout << data;
t.join();
}
promise 에서 future 를 얻어올 수 있음
만약 promise 에 어떤 데이트를 세팅하면
future 에서 데이터를 얻어올 수 있음
미래에 결과물을 반환해달라는 일종의 약속(promise) 하는 것
std::promise<string> promise;
std::future<string> future = promise.get_future();
여기 까지 설정한다음
스레드를 만들어 소유권을 넘겨주면
즉 std::move(promise) 를 promiseWorker 스레드에 인자로 넘겨 준다
thread t(promiseWorker, std::move(promise));
그리고 위 코드에서
promise.set_value("secret message");
를 했음으로 promise 에서 이 데이터를 다음 처럼 받아 올 수 있다
string data = future.get();
std::cout << data;
결과 화면
코드에서 보면 move 하기 전에 promise 가 pending 상태지만 move 하고 난 이후엔 main 에서의 promise 는 소유권을 넘겨주기 때문에 empty 가 되어 더이상 지역변수 promise 를 사용하면 안된다
즉 결과는 fugure.get() 을 통해 넘겨 받게 된다
이렇게 하는 이유는 실수로 여러번 get을 호출하면 문제가 되기 때문에
async 와 차이점이 있다면 promise 는 객체를 만들어서 여기저기 재사용이 가능하다
packaged_task 와 future
promise 와 유사하지만 사용이 간단한 packaged_task 가 있음
void taskWorker(std::packaged_task<int64(void)>&& task)
{
task();
}
//main 어딘가..
{
//promise 와 유사하지만 사용이 간단한 packaged_task 가 있음
std::packaged_task<int64(void)> task(calculate);
std::future<int64> future = task.get_future();
thread t(taskWorker, std::move(task));
std::cout << future.get() << std::endl;;
t.join();
}
결과는
100000
promise 와 차이점이 있다면 동일한 함수타입에 대한 함수들에 task를 넘겨줘서
결과물을 받아올때 유용
https://answer-me.tistory.com/32
ref : https://docs.microsoft.com/ko-kr/cpp/parallel/amp/reference/completion-future-class?view=msvc-170
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
Cache (2) - 코어당 캐시메모리 (0) | 2022.09.12 |
---|---|
Cache (1) - 존재하는 이유와 성능 차이 (0) | 2022.09.11 |
future , async (간략한 비동기,동기 함수 실행) (1) (0) | 2022.09.11 |
condition_variable 예제 (Produce, Consumer) (0) | 2022.09.11 |
Conditional Variable - basic (0) | 2022.09.10 |