什么是異步?
異步就是多個線程是同時執行的,與之相對的就是線程同步,二者都應用在并發的場景上。
異步的特點
異步執行的任務無需等待其他任務完成,其本身是通過非阻塞的方式執行的,不依賴前驅任務,通常用于IO密集型場景。
非阻塞:線程不會被其他線程阻塞;
回調或事件驅動:
1、可以通過最直接采用策略執行回調函數,自動創建和管理線程;
2、還可以通過primise收到那個執行線程設置future的異步結果;
未來類--future
future簡單來說就是存儲線程結果的一個模版類,我們可以通過其get方法獲取其異步編程結果的值;
async策略
執行策略:launch::async代表的是立刻在當前線程處執行異步回調函數;
//執行線程回調函數
double func(){cout<<"我是線程回調函數"<<endl; return 6.6;
}void testfuture(){future<double> ret=async(launch::async,func);//立即執行的策略cout<<"開始睡眠"<<endl;sleep(2);auto res=ret.get();cout<<"future:"<<res<<endl;
}
其中ret接收的就是異步線程回調函數的返回值;
deferred策略
該執行策略代表的是延遲執行,也就是主線程執行到async函數時不會立刻觸發執行異步回調函數,而是在后續調用future的get方法時才會觸發執行回調函數;
#pragma once
#include <future>
#include <iostream>
#include <unistd.h>
using namespace std; //執行線程回調函數
double func(){cout<<"我是線程回調函數"<<endl; return 6.6;
}void testfuture2(){future<double>ret=async(launch::deferred,func);//延遲執行策略cout<<"開始睡眠"<<endl;sleep(2);auto res=ret.get();cout<<"future:"<<res<<endl;
}
這段程序的執行結果就是先打印“開始睡眠“,然后睡眠2s,執行到get處,才執行回調函數,打印"我是線程回調函數",返回res獲取返回值6.6,最后打印"future:6.6";
Promise(承諾未來值)
Promise也是一個模版類,通常配合future使用,用于在線程間傳遞數據或同步異步操作的結果
解決的問題
(1) 線程間結果的傳遞
普通線程函數只能通過參數傳遞輸入數據,但無法直接返回計算結果(除非使用全局變量或指針,但這不夠安全)。
promise
?提供了一種安全、標準化的方式,讓一個線程(生產者)可以設置一個值,而另一個線程(消費者)可以通過?future
?獲取這個值。(2) 異步操作的同步
在異步編程中,我們可能需要等待某個線程完成任務并獲取其結果,而?
promise/future
?提供了一種線程安全的等待機制,避免了手動使用條件變量或鎖。(3) 異常傳遞
如果一個線程在執行過程中拋出異常,可以通過?
promise
?將異常傳遞給?future
,讓調用方能夠捕獲并處理它。
實現方案
promise+move
#include <iostream>
#include <future>
#include <thread>using namespace std;void task(promise<int> pro) {pro.set_value(666);
}int main() {promise<int>ff;//綁定future和promisefuture<int>fut = ff.get_future();thread t(task,move(ff));//獲取結果cout <<"異步future:"<< fut.get() << endl;t.join();return 0;
}
這種方案是比較安全的,因為沒有使用指針和引用,關鍵點在于,中間變量作為參數,回調執行完就會釋放掉參數pro;
那么問題來了,回調執行完就會釋放掉pro,那future還能獲取結果嗎?
答案是可以的!future從promise讀取結果的過程在于set_value寫入,執行回調之前future和prommise已經綁定了共享的關系,而set_value就會同步數據給future;即便是后續參數pro和主線程的ff都無效了,future依舊可以獲取到正確的結果;
promise+ref
#include <iostream>
#include <future>
#include <thread>using namespace std;void task(promise<int>& pro) {pro.set_value(666);
}int main() {promise<int>ff;//綁定future和promisefuture<int>fut = ff.get_future();thread t(task,ref(ff));//獲取結果cout <<"異步future:"<< fut.get() << endl;t.join();return 0;
}
與上面的區別在于回調的參數是引用,而是實參需要使用ref強調傳遞的是引用對象,這種方法因涉及引用,所以沒有使用move安全一些;