C++11 使用 std::async建立非同步程式

來源:互聯網
上載者:User

標籤:other   post   非同步   return   執行   exit   tin   end   pretty   

c++11中增加了線程,使得我們可以非常方便的建立線程,它的基本用法是這樣的:

void f(int n);std::thread t(f, n + 1);t.join();

但是線程畢竟是屬於比較低層次的東西,有時候使用有些不便,比如我希望擷取線程函數的返回結果的時候,我就不能直接通過 thread.join()得到結果,這時就必須定義一個變數,線上程函數中去給這個變數賦值,然後join,最後得到結果,這個過程是比較繁瑣的。 c++11還提供了非同步介面std::async,通過這個非同步介面可以很方便的擷取線程函數的執行結果。std::async會自動建立一個線程去調用 線程函數,它返回一個std::future,這個future中儲存了線程函數返回的結果,當我們需要線程函數的結果時,直接從future中擷取,非 常方便。但是我想說的是,其實std::async給我們提供的便利可不僅僅是這一點,它首先解耦了線程的建立和執行,使得我們可以在需要的時候擷取非同步 操作的結果;其次它還提供了線程的建立策略(比如可以通過消極式載入的方式去建立線程),使得我們可以以多種方式去建立線程。在介紹async具體用法以及 為什麼要用std::async代替線程的建立之前,我想先說一說std::future、std::promise和 std::packaged_task。

std::future

std::future是一個非常有用也很有意思的東西,簡單說std::future提供了一種訪問非同步作業結果的機制。從字面意思來理解, 它表示未來,我覺得這個名字非常貼切,因為一個非同步作業我們是不可能馬上就擷取操作結果的,只能在未來某個時候擷取,但是我們可以以同步等待的方式來擷取 結果,可以通過查詢future的狀態(future_status)來擷取非同步作業的結果。future_status有三種狀態:

  • deferred:非同步作業還沒開始
  • ready:非同步作業已經完成
  • timeout:非同步作業逾時
//查詢future的狀態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);

擷取future結果有三種方式:get、wait、wait_for,其中get等待非同步作業結束並返回結果,wait只是等待非同步作業完成,沒有傳回值,wait_for是逾時等待返回結果。

std::promise

std::promise為擷取線程函數中的某個值提供便利,線上程函數中給外面傳進來的promise賦值,當線程函數執行完成之後就可以通過promis擷取該值了,值得注意的是取值是間接的通過promise內部提供的future來擷取的。它的基本用法:

std::promise<int> pr;    std::thread t([](std::promise<int>& p){ p.set_value_at_thread_exit(9); },std::ref(pr));    std::future<int> f = pr.get_future();    auto r = f.get();
std::packaged_task

std::packaged_task它封裝了一個可調用的目標(如function, lambda expression, bind expression, or another function object),以便非同步呼叫,它和promise在某種程度上有點像,promise儲存了一個共用狀態的值,而packaged_task儲存的是一 個函數。它的基本用法:

std::packaged_task<int()> task([](){ return 7; });    std::thread t1(std::ref(task));     std::future<int> f1 = task.get_future();     auto r1 = f1.get();
std::promise、std::packaged_task和std::future的關係

至此, 我們介紹了std::async相關的幾個對象std::future、std::promise和std::packaged_task,其中 std::promise和std::packaged_task的結果最終都是通過其內部的future返回出來的,不知道讀者有沒有搞糊塗,為什麼有 這麼多東西出來,他們之間的關係到底是怎樣的?且聽我慢慢道來,std::future提供了一個訪問非同步作業結果的機制,它和線程是一個層級的屬於低層 次的對象,在它之上高一層的是std::packaged_task和std::promise,他們內部都有future以便訪問非同步作業結 果,std::packaged_task封裝的是一個非同步作業,而std::promise封裝的是一個值,都是為了方便非同步作業的,因為有時我需要獲 取線程中的某個值,這時就用std::promise,而有時我需要獲一個非同步作業的傳回值,這時就用std::packaged_task。那 std::promise和std::packaged_task之間又是什麼關係呢?說他們沒關係也關係,說他們有關係也有關係,都取決於你了,因為我 可以將一個非同步作業的結果儲存到std::promise中。如果讀者還沒搞清楚他們的關係的話,我就用更通俗的話來解釋一下。比如,一個小夥子給一個姑 娘表白真心的時候也許會說:”我確保 會 給你一個美好的未來“或者”我會努力奮鬥為你創造一個美好的未來“。姑娘往往會說:”我等著“。現在我來將這三句話用c++11來翻譯一下:

小夥子說:我確保會給你一個美好的未來等於c++11中"std::promise a std::future"; 
小夥子說:我會努力奮鬥為你創造一個美好的未來等於c++11中"std::packaged_task a future"; 
姑娘說:我等著等於c++11中"future.get()/wait()";

小夥子兩句話的個中差異,自己琢磨一下,這點差異也是std::promise和std::packaged_task的差異。現實中的山盟海 誓靠不靠得住我不知道,但是c++11中的確保和未來是一定可靠的,發起來了確保就一定有未來。細想起來c++11標準的制定者選定的關鍵字真是貼切而有 意思!好了,插科打諢到此了,現在言歸正傳,回過頭來說說std::async。

為什麼要用std::async代替線程的建立

std::async又是幹啥的,已經有了td::future、std::promise和std::packaged_task,夠多的 了,真的還要一個std::async來湊熱鬧嗎,std::async表示很委屈:我不是來湊熱鬧的,我是來幫忙的。是的,std::async是為了 讓使用者的少費點腦子的,它讓這三個對象默契的工作。大概的工作過程是這樣的:std::async先將非同步作業用std::packaged_task包 裝起來,然後將非同步作業的結果放到std::promise中,這個過程就是創造未來的過程。外面再通過future.get/wait來擷取這個未來的 結果,怎麼樣,std::async真的是來幫忙的吧,你不用再想到底該怎麼用std::future、std::promise和 std::packaged_task了,std::async已經幫你搞定一切了!

現在來看看std::async的原型async(std::launch::async | std::launch::deferred, f, args...),第一個參數是線程的建立策略,有兩種策略,預設的策略是立即建立線程:

  • std::launch::async:在調用async就開始建立線程。
  • std::launch::deferred:消極式載入方式建立線程。調用async時不建立線程,直到調用了future的get或者wait時才建立線程。

第二個參數是線程函數,第三個參數是線程函數的參數。


std::async基本用法:

 1 std::future<int> f1 = std::async(std::launch::async, [](){ 2         return 8; 3     }); 4  5     cout << f1.get() << endl; //output: 8 6  7     std::future<void> f2 = std::async(std::launch::async, [](){ 8         cout << 8 << endl; 9         //return 8;10     });11 12     f2.wait(); //output: 813 14     std::future<int> future = std::async(std::launch::async, [](){15         std::this_thread::sleep_for(std::chrono::seconds(3));16         return 8;17     });18 19     std::cout << "waiting...\n";20     21     //Test12();22     std::future_status status;23     Sleep(3000);24     do {25         status = future.wait_for(std::chrono::seconds(1));26         if (status == std::future_status::deferred) {27             std::cout << "deferred\n";28         }29         else if (status == std::future_status::timeout) {30             std::cout << "timeout\n";31         }32         else if (status == std::future_status::ready) {33             std::cout << "ready!\n";34         }35     } while (status != std::future_status::ready);36 37     std::cout << "result is " << future.get() << ‘\n‘;

 

 

 可能的結果: waiting... timeout timeout ready! result is 8
總結:

std::async是更高層次上的非同步作業,使我們不用關注線程建立內部細節,就能方便的擷取非同步執行狀態和結果,還可以指定線程建立策略,應該用std::async替代線程的建立,讓它成為我們做非同步作業的首選。

C++11 使用 std::async建立非同步程式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.