国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++并發(fā)與多線程筆記八:async、future、packaged_task、promise

這篇具有很好參考價值的文章主要介紹了C++并發(fā)與多線程筆記八:async、future、packaged_task、promise。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1 前言

本文接上文 C++并發(fā)與多線程筆記七:condition_variable、wait、notify_one/all 的內容,主要記錄 async、future、packaged_task、promise 概念以及用法。

2 std::async、std::future 創(chuàng)建后臺任務并返回值

2.1 基本用法

std::async 是個函數(shù)模板,用來啟動一個異步任務,啟動一個異步任務后,它返回一個 std::future 類模板對象。

上述"啟動一個異步任務"的本質含義就是自動創(chuàng)建一個線程并開始執(zhí)行對應的線程入口函數(shù),它返回一個 std::future 類模板對象,這個對象內就含有線程入口函數(shù)的返回值(即線程執(zhí)行的返回結果)。

我們可以通過調用 std::future 對象的成員函數(shù) get() 來獲取結果。即 std::future 提供了一種訪問異步操作結果的機制,就是說這個結果可能沒辦法馬上拿到,在線程執(zhí)行完畢后,才能拿到。

注:std::future 對象里會保存一個值,在將來的某個時刻能夠拿到。

在下面的示例代碼中,std::future 對象的 get() 成員函數(shù)等待線程結束并返回結果,也就是說 get() 函數(shù)在拿不到值時是阻塞的。另外,std::future 對象還有個 wait() 成員函數(shù),等待線程返回,但函數(shù)本身并不返回結果,類似線程對象中的 join() 函數(shù)。

#include <thread>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

/* 線程入口函數(shù) */
int myThread() {
  cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
  std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
  std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
  cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
  return 5;
}

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  /* 定義一個std::future類模板對象,變量類型為 int, */
  /* 并通過 std::async 啟動一個異步任務 myThread */
  std::future<int> result = std::async(myThread);

  cout << "continue......!" << endl;

  /* get() 函數(shù)會阻塞在這,等待 myThread 線程執(zhí)行完畢后返回  */
  cout << result.get() << endl;
  // result.wait(); /* 等待線程執(zhí)行完畢,但不返回值 */

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;
  return 0;
}

輸出結果:

main() start thread ID = 1860
continue......!
myThread() start thread ID = 3300
# 這里會阻塞 5000 ms
myThread() end thread ID = 3300
5
main() end thread ID = 1860

注:std::future 對象的 get() 成員函數(shù)只能調用一次,否則運行時會發(fā)生異常:std::future_error。

使用類成員函數(shù)作為 std::async 啟動的線程回調函數(shù)有些額外的細節(jié),示例代碼如下,具體詳見注釋:

#include <thread>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

class A {
 public:
  /* 線程入口函數(shù) */
  int myThread(int myParameter) {
    cout << myParameter << endl;
    cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
    std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
    cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
    return 5;
  }
};

int main() {
  A objA;
  int tempParameter = 12;
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  /* std::async 傳入類成員函數(shù)作為線程回調函數(shù):第二個參數(shù)為類對象引用,第三個參數(shù)開始為線程回調函數(shù)的入?yún)?*/
  std::future<int> result = std::async(&A::myThread, &objA, tempParameter);

  /* std::async 傳入全局函數(shù)作為線程回調函數(shù):第二個參數(shù)開始為線程回調函數(shù)的入?yún)?*/
  // std::future<int> result = std::async(myThread, tempParameter);

  cout << "continue......!" << endl;
  cout << result.get() << endl;

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;
  return 0;
}

如果使用 std::async 啟動異步任務后,不調用 std::futureget() 函數(shù)或 wait() 函數(shù),程序在主線程退出前依舊會等待異步任務(子線程)結束,但通常不建議這樣操作,可能會有風險。

2.2 擴展用法

2.2.1 std::launch::deferred

我們可以通過額外向 std::async 傳遞一個 std::launch 類型(枚舉)參數(shù)來達到一些特殊目的。比如 std::launch::deferred 表示線程入口函數(shù)調用被延遲到 std::future 對象的 get() 或者 wait() 函數(shù)被調用時才執(zhí)行。

/*......*/
std::future<int> result = std::async(std::launch::deferred, &A::myThread, &objA, tempParameter);
cout << result.get() << endl;
// result.wait()
/*......*/

輸出結果:

main() start thread ID = 2572
continue......!
12
myThread() start thread ID = 2572
myThread() end thread ID = 2572
5
main() end thread ID = 2572

可以看到,異步任務(線程)中打印的線程 ID 與主線程 ID 一樣,也就是說 std::async 傳入 std::launch::deferred 參數(shù)后,調用 std::future 對象的 get() 或者 wait() 函數(shù)啟動異步任務(線程),實際上并沒有創(chuàng)建新的線程,只起到一個延遲執(zhí)行任務的功能。

注:如果 std::async 傳入 std::launch::deferred 參數(shù)后,沒有調用 std::future 對象的 get() 或者 wait() 函數(shù),那么這個異步任務(線程)不會被創(chuàng)建,也不會執(zhí)行。

2.2.2 std::launch::async

std::launch 還有另一個枚舉類型 async,它是默認參數(shù),表示調用 std::async 函數(shù)時就開始創(chuàng)建線程,并立即開始執(zhí)行,也就是上文一開始講的用法。

3 std::packaged_task

std::packaged_task 是個類模板,它的模板參數(shù)是各種可調用對象。通過 std::packaged_task 來把各種可調用對象包裝起來,方便將來作為線程入口函數(shù)調用,并且 std::packaged_task 包裝的對象中可以拿到線程的 std::future 對象,示例代碼如下:

#include <thread>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

/* 線程入口函數(shù) */
int myThread(int myParameter) {
  cout << myParameter << endl;
  cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
  std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
  std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
  cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
  return 5;
}

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  /* 把 myThread 函數(shù)通過 std::packaged_task 包裝起來 */
  /* myThread 函數(shù)的返回值和入?yún)⒍际?int 類型,因此模板類型為 int(int) */
  std::packaged_task<int(int)> myThreadTask(myThread);

  /* 使用包裝好的 myThreadTask 對象,第二個參數(shù)為線程回調函數(shù)的入?yún)?,?chuàng)建線程并開始執(zhí)行  */
  std::thread mThreadObj(std::ref(myThreadTask), 12);

  mThreadObj.join(); /* 等待線程結束 */

  /* 根據(jù) std::packaged_task 包裝的對象獲取 std::future 對象 */
  std::future<int> result = myThreadTask.get_future();
  cout << result.get() << endl;

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;

  return 0;
}

輸出結果:

main() start thread ID = 7512
12
myThread() start thread ID = 3916
myThread() end thread ID = 3916
5
main() end thread ID = 7512

使用 std::packaged_task 包裝 lambda 表達式的示例代碼如下,這個 lambda 表達式的功能與上文的 myThread() 函數(shù)一樣:

  /* 把 lambda 表達式通過 std::packaged_task 包裝起來 */
  std::packaged_task<int(int)> myThreadTask([](int myParameter) {
    cout << myParameter << endl;
    cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
    std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
    cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
    return 5;
  });

std::packaged_task 包裝起來的可調用對象是可以直接調用的,所以從這個角度來講,std::packaged_task 對象也是一個可調用對象,比如:

#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  /* 把 lambda 表達式通過 std::packaged_task 包裝起來 */
  std::packaged_task<int(int)> myThreadTask([](int myParameter) {
    cout << myParameter << endl;
    cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
    std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
    cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
    return 5;
  });

  /* 直接執(zhí)行,并獲取函數(shù)返回值 */
  myThreadTask(105);
  std::future<int> result = myThreadTask.get_future();
  cout << result.get() << endl;

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;

  return 0;
}

輸出結果:

main() start thread ID = 15236
105
myThread() start thread ID = 15236
myThread() end thread ID = 15236
5
main() end thread ID = 15236

std::packaged_task 可以實現(xiàn)很多靈活的操作,比如創(chuàng)建一個容器,里面可以放入很多 std::packaged_task 對象,需要的時候再拿出來用:

#include <vector>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

vector<std::packaged_task<int(int)>> myTasks; /* 容器 */

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  /* 把 lambda 表達式通過 std::packaged_task 包裝起來 */
  std::packaged_task<int(int)> myThreadTask([](int myParameter) {
    cout << myParameter << endl;
    cout << "myThread() start thread ID = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
    std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */
    cout << "myThread() end thread ID = " << std::this_thread::get_id() << endl;
    return 5;
  });

  /* 將 myThreadTask 對象移動到容器中,移動完畢后容器size為1,myThreadTask為空 */
  /* 此處只能用 move,不能用 copy,因為 std::packaged_task 的拷貝構造函數(shù)被指定為刪除的(delete),只能移動或者引用,無法拷貝。 */
  myTasks.push_back(std::move(myThreadTask));

  /* ...... */

  /* 將容器中的 std::packaged_task 對象取出來用 */
  std::packaged_task<int(int)> myThreadTaskBak;
  auto iter = myTasks.begin();        /* 用迭代器獲取容器中的第一個元素 */
  myThreadTaskBak = std::move(*iter); /* 同樣用 move 取出來 */
  myTasks.erase(iter);                /* 刪除容器中的第一個元素,后續(xù)代碼不可以在再使用 iter */
  myThreadTaskBak(123);               /* 調用拿出來的 std::packaged_task 對象 */

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;

  return 0;
}

輸出結果:

main() start thread ID = 13080
123
myThread() start thread ID = 13080
myThread() end thread ID = 13080
main() end thread ID = 13080

4 std::promise

std::promise 也是一個類模板,我們可以在某個線程中給它賦值,然后我們可以在其他線程中把這個值取出來,示例代碼如下:

#include <thread>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

void myThread(std::promise<int>& promiseValue, int calc) {
  int result;

  calc++;
  calc *= 10;

  /* 這里可以做各種運算,假設事件花了 5000 ms */
  std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
  std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */

  /* 計算出結果后,將其保存到 std::promise 對象中 */
  result = calc;
  promiseValue.set_value(result);

  return;
}

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  int calc = 128;
  /* 聲明一個 std::promise 對象,保存的值類型為 int */
  std::promise<int> myPpromiseValue;

  /* 創(chuàng)建并執(zhí)行線程函數(shù),等待其結束 */
  std::thread myThreadObj(myThread, std::ref(myPpromiseValue), 128);
  myThreadObj.join();  /* 用 std::thread 類型的對象,必須用 join 等待線程執(zhí)行完畢 */

  /* promise 和 future 綁定,用于獲取線程返回值 */
  std::future<int> result = myPpromiseValue.get_future();
  cout << result.get() << endl; /* get 只能調用一次  */

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;

  return 0;
}

輸出結果:

main() start thread ID = 7520
1290
main() end thread ID = 7520

用法簡單來說就是:通過 std::promise 保存一個值,在將來某個時刻我們通過把一個 std::future 綁定到這個 std::promise 上來得到綁定值。

也可以在上述代碼中加多一個線程,在第二個子線程中獲取第一個子線程設置的 std::promise 對象值,改造后的代碼如下:

#include <thread>
#include <future> /* 需要包含該頭文件 */
#include <iostream>
using namespace std;

void myThread(std::promise<int>& promiseValue, int calc) {
  int result;

  calc++;
  calc *= 10;

  /* 這里可以做各種運算,假設事件花了 5000 ms */
  std::chrono::milliseconds duration(5000); /* 定義間隔時間為 5000 ms */
  std::this_thread::sleep_for(duration);    /* 睡眠指定的間 */

  /* 計算出結果后,將其保存到 std::promise 對象中 */
  result = calc;
  promiseValue.set_value(result);

  return;
}

void myThreadEx(std::future<int>& futureValue) {
  auto result = futureValue.get();
  cout << "myThreadEx result = " << result << endl;
  return;
}

int main() {
  cout << "main() start thread ID = " << std::this_thread::get_id() << endl;

  int calc = 128;
  /* 聲明一個 std::promise 對象,保存的值類型為 int */
  std::promise<int> myPpromiseValue;

  /* 創(chuàng)建并執(zhí)行線程函數(shù),等待其結束 */
  std::thread myThreadObj(myThread, std::ref(myPpromiseValue), 128);
  myThreadObj.join();

  /* promise 和 future 綁定,用于獲取線程返回值 */
  std::future<int> result = myPpromiseValue.get_future();

  /* 創(chuàng)建并執(zhí)行第二個線程,等待其結束 */
  std::thread myThreadObjEx(myThreadEx, std::ref(result));
  myThreadObjEx.join();

  cout << "main() end thread ID = " << std::this_thread::get_id() << endl;

  return 0;
}

輸出結果:

main() start thread ID = 2892
myThreadEx result = 1290
main() end thread ID = 2892

std::promise 可以在線程與線程之間傳遞各種類型的數(shù)據(jù)。

5 總結

C++ 并發(fā)與多線程的筆記中介紹了很多庫中帶的類型和函數(shù),但學習這些東西的目的,并不是要把它們都用在自己的實際開發(fā)中,相反,如果能用最少的東西寫出一個穩(wěn)定、高效的多線程程序,這是更好的。代碼寫的優(yōu)雅整潔的最終目的是給人看的,能讓別人輕易看懂并理解的代碼才是好代碼。

我們?yōu)榱顺砷L,必須要閱讀一些高手寫的代碼,從而快速實現(xiàn)自己的代碼積累,等量變發(fā)生質變時,我們的技術會有一個大幅的提升。

此處學習這些內容的目的主要是方便我們未來能夠讀懂高手的代碼,至少看別人代碼,遇到 std::promise、std::future 等東西時不會一頭霧水。文章來源地址http://www.zghlxwxcb.cn/news/detail-412341.html

到了這里,關于C++并發(fā)與多線程筆記八:async、future、packaged_task、promise的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • C++11并發(fā)與多線程筆記 (1)

    C++11并發(fā)與多線程筆記 (1)

    指在一個時間段內有多個進程在執(zhí)行 兩個或者更多的任務(獨立的活動)同時發(fā)生(進行):一個程序同時執(zhí)行多個獨立的任務; 以往計算機,單核cpu(中央處理器):某一個時刻只能執(zhí)行一個任務,由操作系統(tǒng)調度,每秒鐘進行多次所謂的“任務切換”。并發(fā)的假象( 不

    2024年02月12日
    瀏覽(28)
  • C++11并發(fā)與多線程筆記(3)線程傳參詳解,detach()大坑,成員函數(shù)做線程函數(shù)

    C++11并發(fā)與多線程筆記(3)線程傳參詳解,detach()大坑,成員函數(shù)做線程函數(shù)

    在使用detach時,不推薦引用傳遞,指針傳遞肯定有問題 在創(chuàng)建線程的同時構造臨時對象的方法傳遞參數(shù)是可行的 如果傳遞int這種基本數(shù)據(jù)類型,推薦使用 值傳遞 ,不要用引用 如果傳遞 類對象 ,避免使用隱式類型轉換,全部都是創(chuàng)建線程這一行就創(chuàng)建出 臨時對象 ,然后在

    2024年02月12日
    瀏覽(20)
  • C++11并發(fā)與多線程筆記(6) unique_lock(類模板)

    unique_lock 是一個類模板。 unique_lock 比 lock_guard 靈活很多 ( 多出來很多用法 ),效率差一點,內存占用多一些。 使用: unique_lockmutex myUniLock(myMutex); std::adopt_lock:標記作用,表示這個互斥量已經被lock()(方便記憶:已經被lock()收養(yǎng)了,不需要再次lock() ),即 不需要在構造函

    2024年02月12日
    瀏覽(20)
  • C++11并發(fā)與多線程筆記(7) 單例設計模式共享數(shù)據(jù)分析、解決,call_once

    程序靈活,維護起來可能方便,用設計模式理念寫出來的代碼很晦澀,但是別人接管、閱讀代碼都會很痛苦 老外應付特別大的項目時,把項目的開發(fā)經驗、模塊劃分經驗,總結整理成設計模式 中國零幾年設計模式剛開始火時,總喜歡拿一個設計模式往上套,導致一個小小的

    2024年02月12日
    瀏覽(23)
  • 分布式集群與多線程高并發(fā)

    ? 后臺數(shù)據(jù)的處理語言有很多,Java 是對前端采集的數(shù)據(jù)的一種比較常見的開發(fā)語言。互聯(lián)網移動客戶端的用戶量特別大,大量的數(shù)據(jù)處理需求應運而生。可移動嵌入式設備的表現(xiàn)形式?? 很多,如 PC 端,手機移動端,智能手表,Google? 眼鏡等。Server2client 的互聯(lián)網開發(fā)模式比

    2024年02月08日
    瀏覽(23)
  • Python中的并發(fā)編程:多線程與多進程的比較【第124篇—多線程與多進程的比較】

    Python中的并發(fā)編程:多線程與多進程的比較【第124篇—多線程與多進程的比較】

    在Python編程領域中,處理并發(fā)任務是提高程序性能的關鍵之一。本文將探討Python中兩種常見的并發(fā)編程方式:多線程和多進程,并比較它們的優(yōu)劣之處。通過代碼實例和詳細的解析,我們將深入了解這兩種方法的適用場景和潛在問題。 多線程是一種輕量級的并發(fā)處理方式,適

    2024年03月14日
    瀏覽(20)
  • 再見了Future,圖解JDK21虛擬線程的結構化并發(fā)

    再見了Future,圖解JDK21虛擬線程的結構化并發(fā)

    Java為我們提供了許多啟動線程和管理線程的方法。在本文中,我們將介紹一些在Java中進行并發(fā)編程的選項。我們將介紹 結構化并發(fā) 的概念,然后討論 Java 21 中一組預覽類——它使將任務拆分為子任務、收集結果并對其進行操作變得非常容易,而且不會不小心留下任何掛起的

    2024年02月05日
    瀏覽(25)
  • 《C++并發(fā)編程實戰(zhàn)》讀書筆記(1):線程管控

    包含頭文件 thread 后,通過構建 std::thread 對象啟動線程,任何可調用類型都適用于 std::thread 。 啟動線程后,需要明確是等待它結束、還是任由它獨自運行: 調用成員函數(shù) join() 會先等待線程結束,然后隸屬于該線程的任何存儲空間都會被清除, std::thread 對象不再關聯(lián)到已結

    2024年02月10日
    瀏覽(20)
  • 【linux 多線程并發(fā)】多線程模型下的信號通信處理,與多進程處理的比較,屬于相同進程的線程信號分發(fā)機制

    ? 專欄內容 : 參天引擎內核架構 本專欄一起來聊聊參天引擎內核架構,以及如何實現(xiàn)多機的數(shù)據(jù)庫節(jié)點的多讀多寫,與傳統(tǒng)主備,MPP的區(qū)別,技術難點的分析,數(shù)據(jù)元數(shù)據(jù)同步,多主節(jié)點的情況下對故障容災的支持。 手寫數(shù)據(jù)庫toadb 本專欄主要介紹如何從零開發(fā),開發(fā)的

    2024年01月17日
    瀏覽(22)
  • 《C++并發(fā)編程實戰(zhàn)》讀書筆記(2):線程間共享數(shù)據(jù)

    在C++中,我們通過構造 std::mutex 的實例來創(chuàng)建互斥量,調用成員函數(shù) lock() 對其加鎖,調用 unlock() 解鎖。但通常更推薦的做法是使用標準庫提供的類模板 std::lock_guard ,它針對互斥量實現(xiàn)了RAII手法:在構造時給互斥量加鎖,析構時解鎖。兩個類都在頭文件 mutex 里聲明。 假設

    2024年02月10日
    瀏覽(11)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包