天天看點

C++ 多線程:future 異步通路類(線程之間安全便捷的資料共享)

文章目錄

  • ​​future前言​​
  • ​​future描述​​
  • ​​future類成員使用​​
  • ​​總結​​

future前言

首先檢視如下代碼

#include <iostream>
#include <thread>
#include <future>
#include <mutex>

using namespace std;

void fun1(int n,int &x) {
    int res = 1;
    for (int i = n; i>1; --i) {
        res *=i;
    }
    cout << "Result is " << res << endl;//輸出最終的結果并指派給共享變量x
    x = res;
}

int main()
{
    int x;
    std::thread t1(fun1,4,std::ref(x));
    t1.join();
    return 0;
}      

這段代碼僅擁有一個線程,但是可以很明顯得看到主線程和t1線程想要共享同一個變量x,同時x是需要在t1線程進行指派将結果傳回給主線程,由主線程來擷取。這樣的線程之間的變量傳遞,我們所知道的傳遞方式隻能是傳遞引用。同時僅僅傳遞引用是不夠的,因為線程之間的變量共享需要對變量進行保護才能安全有效共享,這樣就需要引入​

​mutex​

​​互斥變量和​

​condition_variable​

​條件變量對共享變量進行保護。

這樣會導緻代碼邏輯更加複雜,邏輯細節也會變得非常謹慎。這個時候我們引入​

​future​

​類對以上操作進行簡化。

future描述

  • 頭檔案 ​

    ​<future>​

  • 聲明​

    ​template< class T > class future<T&>;​

  • 簡介

    ​future​

    ​類模版提供了一種異步通路操作機制如下:
  • 異步操作能提供一個 std::future 對象給該異步操作的建立者。其中包括​

    ​std::async​

    ​​ 、 ​

    ​std::packaged_task​

    ​​ 或 ​

    ​std::promise​

    ​​的傳回值為​

    ​std::future​

    ​對象
  • 異步操作的建立者能用各種方法查詢、等待或從 std::future 提取值。若異步操作仍未提供值,則這些方法可能阻塞。如使用​

    ​std::async​

    ​​建立的異步操作傳回的​

    ​future​

    ​​對象未擷取值,那麼​

    ​std::async​

    ​将不會退出
  • 異步操作準備好發送結果給建立者時,它能通過修改連結到建立者的 ​

    ​std::future​

    ​​ 的共享狀态(例如​

    ​std::promise::set_value​

    ​)進行

future類成員使用

  • ​std::future<T>::get​

    ​​ 擷取異步操作的傳回結果

    get 方法等待直至 future 擁有合法結果并(依賴于使用哪個模闆)擷取它。它等效地調用 wait() 等待結果

    這裡需要簡單說明下get函數的傳回值,它的傳回值為​

    ​std::move()​

    ​類型,即将之前的傳回結果包括值和位址都進行指派。如果二次嘗試​

    ​get​

    ​,回通路到空的值和位址,報出異常​

    ​std::future_error​

    ​。

    我們對剛開始的代碼更改如下:

#include <iostream>
#include <thread>
#include <future>
#include <mutex>

using namespace std;

int fun1(int n) {
    int res = 1;
    for (int i = n; i>1; --i) {
        res *=i;
    }
    cout << "Result is " << res << endl;
    return res;
}

int main()
{
    int x;
    //std::thread t1(fun1,4,std::ref(x));
    //t1.join();
    //我們使用異步操作,并使用get函數擷取異步操作函數的合法傳回值
    std::future<int> fu = std::async(fun1,4);
    x = fu.get();
    return 0;
}      

如上代碼,我們使用​

​std::async()​

​​異步操作來建立字線程,并傳回一個future類的對象,然後由​

​future​

​​對象的​

​get()​

​函數來擷取子線程合法的傳回結果。

關于​

​async()​

​​等異步操作将會在下一篇進行分享,這裡簡單描述一下:

​​

​async​

​​提供的是從子線程擷取數值到主線程,如果我們想要從主線程擷取數值到子線程,則需要​

​promise​

​​異步操作

檢視如下代碼為我們利用​​

​promise​

​從主線程擷取數值到子線程

#include <iostream>
#include <thread>
#include <future>
#include <mutex>

using namespace std;

int fun1(std::future<int> &f) {
    int res = 1;
    int n = f.get();
    for (int i = n; i>1; --i) {
        res *=i;
    }
    cout << "Result is " << res << endl;
    return res;
}

int main()
{
    int x;
    //std::thread t1(fun1,4,std::ref(x));
    //t1.join();
    std::promise<int> p;
    //标示f是一個需要從未來擷取數值future對象
    std::future<int> f = p.get_future(); 
    
    std::future<int> fu = std::async(std::launch::async,fun1, std::ref(f));
    //為f設定數值,在子線程中進行f.get()擷取主線程到數值
    p.set_value(4);
    x = fu.get();
    cout << "Get from child " << x << endl;
    return 0;
}      

輸出結果如下:

Result is 24
Get from child 24      
  • ​std::future<T>::wait​

    ​ 阻塞線程,直到擷取到可用結果
#include <iostream>
#include <future>
#include <thread>
 
int fib(int n)
{
  if (n < 3) return 1;
  else return fib(n-1) + fib(n-2);
}
 
int main()
{
    std::future<int> f1 = std::async(std::launch::async, [](){
        return fib(20);
    });
    std::future<int> f2 = std::async(std::launch::async, [](){
        return fib(25);
    });
 
    std::cout << "waiting...\n";
    //主線程在該處阻塞,直到擷取到f1,f2兩個線程到合法的結果
    f1.wait();
    f2.wait();
 
    std::cout << "f1: " << f1.get() << '\n';
    std::cout << "f2: " << f2.get() << '\n';
}      
  • ​wait_for()​

    ​​和​

    ​wait_until()​

    ​兩個函數成員同​

    ​condition_variable​

    ​中的類似,仍然是僅增加了需要等待的時間,到達等待的時間如仍然無法擷取到合法的傳回值即同樣終止​

    ​get​

總結