天天看點

C++:并發與多線程(十)一 原子操作 std:atomic續談二 std: async深入談

std: atomic續談、std: async深入談

  • 一 原子操作 std:atomic續談
  • 二 std: async深入談
    • 2.1 std: async參數詳述
    • 2.2 std: async和std: thread的差別
    • 2.3 std async不确定性問題的解決

一 原子操作 std:atomic續談

不是所有運算符都支援 比如不支援 a = a+1

二 std: async深入談

2.1 std: async參數詳述

std::thread() 如果系統資源緊張,可能會建立線程失敗,執行std::thread()時程式可能會崩潰。

std::async 一般稱之為建立一個異步任務,async有時并不建立新線程

如果用std::launch::deferred來調用async會怎麼樣,會延遲調用,并且不建立新線程,如果future不調用.wait或.get,則不執行async的入口函數

如果用std::launch::async 強制這個異步任務在新線程上執行,系統必須建立新線程。

std::launch::async |std::launch::deferred 當參數是這樣時,意味着async的行為可能是其中任意一個,由編譯器實作

當不傳入參數時,預設參數為std::launch::async |std::launch::deferred,具體使用哪個由編譯器控制。

#include <iostream>
#include <string>
#include <future>
#include <thread>
#include <mutex>
#include <atomic>
using namespace  std;

int mythread()
{
    cout<<"mythread start"<<"thread_id:"<<std::this_thread::get_id<<endl;
    return 1;
}


int main()
{
     cout<<"main thread_id:"<<std::this_thread::get_id<<endl;
    std::future<int>result = std::async(std::launch::deferred,mythread);
    cout<<result.get()<<endl;
    cout<<"ok"<<endl;
    return 0;

}

           

列印:

G:\CSlrn\C++Code\MultiThread\2_11\cmake-build-debug\2_11.exe
main thread_id:1
mythread startthread_id:1
1
ok

Process finished with exit code 0
           

2.2 std: async和std: thread的差別

  • std::thread建立線程,如果系統資源緊張,建立線程失敗,那麼整個程式就會報異常并崩潰
  • std::thread建立線程,如果線程有傳回值,不友善拿到該傳回值
  • std::async建立異步任務,可能建立線程,也可能不建立線程,并且該方法很容易拿到線程入口函數的傳回值。

    由于系統資源限制

  • (1)如果std::thread建立的線程太多,則可能建立失敗,系統報告異常
  • (2)std::async一般不會異常,如果系統資源緊張時,不加額外參數的async就不會建立新線程,而是後續誰調用了result.get來請求結果,那麼這個異步任務就會運作在調用get語句的線程中執行。
  • (3)一個程式裡,線程數量不宜超過100-200

2.3 std async不确定性問題的解決

  • 不加額外參數的std::async調用,讓系統自行決定是否建立新線程
  • 問題焦點:std::futureresult2 = std::async(mythread);這個寫法
  • 這個異步任務到底有沒有被推遲執行 ,使用上節課的wait_for語句可以判斷
int mythread()
{
    cout<<"mythread start"<<"thread_id:"<<std::this_thread::get_id<<endl;
    std::chrono::seconds dura(5);
    std::this_thread::sleep_for(dura);
    return 1;
}
           
cout<<"main thread_id:"<<std::this_thread::get_id<<endl;

    std::future<int>result = std::async(std::launch::deferred,mythread);
    std::future_status status = result.wait_for(std::chrono::seconds(2));
    if (status == std::future_status::timeout)
    {
        //線程沒有被推遲 但是還沒執行完
        cout<<"time out"<<endl;
    }
    else  if (status == std::future_status::ready)
    {
        //線程沒有被推遲,并且執行完了
        cout<<"thread over"<<endl;
    }
    else  if (status == std::future_status::deferred)
    {
        //線程被延遲了,需要由result.get()來啟動線程
        cout<<"thread deferred"<<endl;
        result.get();
    }
    cout<<"ok"<<endl;
    return 0;
           
G:\CSlrn\C++Code\MultiThread\2_11\cmake-build-debug\2_11.exe
main thread_id:1
thread deferred
mythread startthread_id:1
ok

Process finished with exit code 0

           

在代碼調試中,當我們把額外參數設定為std::launch::async時,分别列印主線程id和子線程id,發現id是一樣的,那麼我們要判斷是否啟動了子線程,除了用wait_for函數來判斷,我們還可以用延時語句來判斷,比如下面這個例子

線程入口函數沒延遲5s再列印線程id

int mythread()
{

    std::chrono::seconds dura(5);
    std::this_thread::sleep_for(dura);
    cout<<"mythread start"<<"thread_id:"<<std::this_thread::get_id<<endl;
    return 1;
}
           

main函數中,等待5s再執行reslut.get(),此時參數為deferred,可以預料到,當主線程等待5s後才會執行子線程,運作起來後,可以大概數一下秒數。

cout<<"main thread_id:"<<std::this_thread::get_id<<endl;

    std::future<int>result = std::async(std::launch::deferred,mythread);
    std::chrono::seconds dura(5);
    std::this_thread::sleep_for(dura);
    cout<<"result="<<result.get()<<endl;
    cout<<"ok"<<endl;
    return 0;
           

結果就是在列印完主線程id後,10s過後才會列印子線程id,列印reslut,列印ok,說明系統是沒有建立子線程的。

G:\CSlrn\C++Code\MultiThread\2_11\cmake-build-debug\2_11.exe
main thread_id:1
mythread startthread_id:1
result=1
ok

Process finished with exit code 0
           

如果我們将參數改為std::launch::async,由std::launch::async的異步性可以知道,一建立std::async就會啟動子線程,子線程内等待的5s和主線程内等待的5s幾乎是同時開始計時的,是以總共隻需要5s就會列印子線程id和reslut ,以及ok,我們就可以判斷,std::launch::async确實建立了子線程

G:\CSlrn\C++Code\MultiThread\2_11\cmake-build-debug\2_11.exe
main thread_id:1
mythread startthread_id:1
result=1
ok

Process finished with exit code 0