《深入應用C++11》筆記-異步線程操作std::async、std::future、std::promise、std::packaged_task

上一篇:《深入應用C++11》筆記-互斥量std::mutex、鎖std::lock_guard

 

std::async和std::future

 

std::async()是一個接受回調函數或函數對象作爲參數的函數模板,並可以異步執行它們。通過這個異步接口可以很方便的獲取線程函數的執行結果,std::async會自動創建一個線程去調用線程函數,它返回一個std::future。

 

std::future中存儲了線程函數返回的結果,當我們需要線程函數的結果時,直接從future中獲取。std::async首先解耦了線程的創建和執行,使得我們可以在需要的時候獲取異步操作的結果;其次它還提供了線程的創建策略(比如可以通過延遲加載的方式去創建線程),可以以多種方式去創建線程。

 

 

 

template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);12

 

std::async中的第一個參數是啓動策略,它控制std::async的異步行爲,共有三類:

 


std::launch::async:立即開始執行線程
std::launch::deferred:調用get()函數時纔開始執行線程
std::launch::async | std::launch::deferred:默認行爲。有了這個啓動策略,它可以異步運行或不運行,這取決於系統的負載,但我們無法控制它。

 

 

 


#include <iostream>
#include <future>
#include <chrono>

 

int func()
{
    std::cout << "run async" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    return 1;
}

 

int main()
{
    auto handle = std::async(std::launch::async, func);
    // 用deferred輸出結果順序將會不同
    //auto handle = std::async(std::launch::deferred, func);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "do something" << std::endl;
    std::cout << handle.get() << std::endl;

 

    return 0;
}

 

輸出結果:
run async
do something
1123456789101112131415161718192021222324252627

 

可以看到,std::async異步執行了func函數,並且能夠通過handle獲取到func函數的返回值。

 

返回值handle實際上是std::future模板類對象,可使用std::future::get獲取結果,如果調用過程中,任務尚未完成,則主線程阻塞至任務完成。也可使用std::future::wait_for等待結果返回,wait_for可設置超時時間,如果在超時時間之內任務完成,則返回std::future_status::ready狀態;如果在超時時間之內任務尚未完成,則返回std::future_status::timeout狀態。更加詳細的用法參考:https://blog.csdn.net/watson2016/article/details/52860797

 

std::promise

 

promise 對象可以保存某一類型 T 的值,該值可被 future 對象讀取。可以通過 get_future 來獲取與該 promise 對象相關聯的 future 對象,調用該函數之後,兩個對象共享相同的共享狀態(shared state)。

 

 

 

#include <iostream>
#include <functional>
#include <thread>
#include <future>

 

void func(std::promise<int>& pro){
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    pro.set_value(10);                         // 設置值
}

 

int main()
{
    std::promise<int> pro;
    std::thread t(func, std::ref(pro));

 

    std::future<int> fut = pro.get_future();
    std::cout << fut.get() << std::endl;      // 10,調用get函數等待pro設置值
    t.join();

 

    return 0;
}123456789101112131415161718192021

 

另外std::promise還提供了set_exception函數,讓future對象的get函數拋出異常;提供set_value_at_thread_exit用於在線程退出時設置值,也就是說直到線程退出get阻塞纔會結束,並且std::promise對象不能在線程結束前被釋放。

 

std::packaged_task

 

std::packaged_task 和 std::promise很類似,不過他包裝一個可調用的對象而不是類型對象,並且允許異步獲取該可調用對象產生的結果。

 

 

 

int func(int from, int to) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return from - to;
}

 

int main()
{
    std::packaged_task<int(int, int)> task(func);
    std::future<int> ret = task.get_future();

 

    std::thread th(std::move(task), 10, 5);
    std::cout << ret.get() << std::endl;          // 5,調用get函數等待func的返回值

 

    th.join();

 

    return 0;
}1234567891011121314151617

 

valid函數,判斷是否有效,對於由默認構造函數生成的 packaged_task 對象,該函數返回 false:

 

 

 

std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
{
    if (tsk.valid()) {                         // 無效返回false
        std::future<int> ret = tsk.get_future();
        std::thread(std::move(tsk), arg).detach();
        return ret;
    }
    else return std::future<int>();
}

 

int main()
{
    std::packaged_task<int(int)> tsk;         // 沒有給packaged_task初始化

 

    std::future<int> fut = launcher(tsk, 25);

 

    if (fut.valid())                          // 無效返回false
    {
        std::cout << << fut.get() << std::endl;
    }

 

    return 0;
}1234567891011121314151617181920212223

std::packaged_task還提供了reset函數,用於重置std::packaged_task狀態,讓他能夠再次調用;提供make_ready_at_thread_exit函數,直到線程退出get阻塞纔會結束,而不是可調用對象結束時。
---------------------
作者:WizardtoH
原文:https://blog.csdn.net/WizardtoH/article/details/81635669
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章