(C++11/14/17學習筆記):std::atomic續、std::async與std::thread對比

Asinmy發表於2020-12-13

目錄

std::atomic續、std::async與std::thread對比

原子操作std::atomic續談

std::async與std::thread對比

std::async引數詳述 ,async用來建立 一個非同步任務

std::async和std::thread的區別(重點)

std::async不確定性問題的解決

總結

std::atomic續、std::async與std::thread對比

原子操作std::atomic續談

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

std::atomic<int> my_count{ 0 };  //封裝了一個int型別的物件(值),可以像操作一個int型別的變數一樣操作my_count

void my_thread()  //執行緒入口函式
{
    for (int i = 0; i < 1000000; i++)
    {
       // my_count++;  //對應的是一個原子操作,不會被打斷
        my_count += 1;
    }
    return;
}

int main()
{
    std::thread threadObj1(my_thread);
    std::thread threadObj2(my_thread);

    threadObj1.join();
    threadObj2.join();

    std::cout << "兩個執行緒執行完畢,最終 my_count的值是" << my_count << std::endl;

    return 0;
}

  • 上述my_count++ ,及my_count += 1;都能保證是原子操作,結果正確,如果改為my_count = my_count + 1;則結果不正確:

  • 一般atomic原子操作,針對++,--,+=,&=,|=,^=是支援的。其他的可能不支援。

std::async與std::thread對比

std::async引數詳述 ,async用來建立 一個非同步任務

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

using namespace std;

int mythread() //執行緒入口函式
{
    cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //列印執行緒id
  
    cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;    //列印執行緒id
    return 100;
}

int main()
{
    cout << "MianThreadID = " << std::this_thread::get_id() << endl;  //列印主執行緒id
    std::future<int> ret = std::async(mythread);  //建立執行緒並開始執行
                   //將future物件與async建立的執行緒繫結到一起,流程不卡在這裡,繼續執行
    std::cout << ret.get() << std::endl;
  
    std::cout << "主執行緒結束" << std::endl;
    return 0;
}
  • 引數 std::launch::deferred延遲呼叫】 ,以及std::launch::async強制建立一個執行緒
  • 使用std::thread() 建立執行緒,如果系統資源緊張,那麼可能建立執行緒就會失敗,那麼執行std::thread()時整個程式可能崩潰。
  • std::async()我們一般不叫建立執行緒(解釋async能夠建立執行緒),我們一般叫它建立 一個非同步任務。
  • std::async和std::thread最明顯的不同,就是async有時候並不建立新執行緒。
    • a)如果你用std::launch::deferred來呼叫async會怎麼樣?
      • std::launch::deferred延遲呼叫,並且不建立新執行緒延遲到future物件呼叫 get()或者 wait() 的時候才執行mythread()。
      • 如果沒有呼叫get或者wait,那麼這個mythread()不會執行
    • b)std::launch::async強制這個非同步任務新執行緒上執行,這 意味著,系統必須要給我建立出新執行緒來執行mythread()。
    • c)std::launch::async | std::launch::deferred,這裡這個 | :意味著呼叫async的行為可能是 “ 建立新執行緒並立即執行”  或者 沒有建立新執行緒並且延遲到呼叫 result.get()才開始執行任務入口函式,兩者居其一
    • d)不帶額外引數,只給async函式一個 入口函式名:
      • 預設值應該是std::launch::async | std::launch::deferred;和c)效果完全一致。
      • 換句話說:系統自行決定是非同步(建立新執行緒)還是同步(不建立新執行緒)方式執行。
  • 自行決定是啥意思?系統如何決定是 非同步(建立新執行緒)還是同步(不建立新執行緒)方式執行 ?

std::async和std::thread的區別(重點)

【std::thread】

  • std::thread建立執行緒,如果系統資源緊張,建立執行緒失敗,那麼整個程式就會報異常崩潰(有脾氣)。
  • std::thread建立執行緒的方式,如果執行緒返回值,你想拿到這個值也不容易。

【std::async】

  • std::async建立非同步任務。可能建立也可能不建立執行緒。
  • std::async呼叫方法很容易拿到執行緒入口函式的返回值。

【系統資源限制的影響】

  • (1)如果用std::thread建立的執行緒太多,則可能建立失敗,系統報告異常,崩潰。
  • (2)如果用std::async,一般就不會報異常不會崩潰,因為 如果系統資源緊張導致無法建立新執行緒的時候,std::async這種不加額外引數的呼叫 就不會建立新執行緒。而是後續誰呼叫了result.get()來請求結果,那麼這個非同步任務mythread就執行在執行這條get()語句所在的執行緒上。
    • 如果你強制std::async一定 要建立新執行緒,那麼就必須使用  std::launch::async。承受的代價就是系統資源緊張時,程式崩潰。
  • (3)經驗:一個程式裡,執行緒數量不宜超過100-200,原因是時間片的切換。

std::async不確定性問題的解決

  • 不加額外引數的std::async呼叫 ,讓系統自行決定是否建立新執行緒。
  • 問題焦點在於  std::future<int> result = std::async(mythread); 這種寫法寫法:
    • 這個非同步任務到底有沒有被推遲執行,(即系統選擇的是std::launch::async還是std::launch::deferred方式)。
  • std::future物件的wait_for函式(注意等待 0 秒進行判斷系統採用的策略):
std::future<int>  result = std::async(mythread); //想判斷async到底有沒有建立新執行緒立即執行還是延遲(沒建立新執行緒)執行

std::future_status status = result.wait_for(0s);   //(std::chrono::seconds(0));
if (status == std::future_status::deferred)
{
	//執行緒被延遲執行了(系統資源緊張了,它給我採用std::launch::deferred策略了)
	cout << result.get() << endl; //這個時侯才去呼叫了mythread();
}
else
{
	//任務沒有被推遲,已經開始執行了被,執行緒被建立了;
	if (status == std::future_status::ready)
	{
		//執行緒成功返回
		cout << "執行緒成功執行完畢並返回!" << endl;
		cout << result.get() << endl;
	}
	else if (status == std::future_status::timeout)
	{
		//超時執行緒還沒執行完
		cout << "超時執行緒沒執行完!" << endl;
		cout << result.get() << endl;
	}
}

總結

  • 使用async,可能兩種情況:std::launch::async或者std::launch::deferred,可以通過 future的狀態來進行判斷,利用std::future物件的wait_for函式等待0秒(相當於執行到這裡就立即得到future物件的狀態)。
    • 如果系統採用的是std::launch::deferred方式,那麼std::future物件就是std::launch::deferred狀態。
    • 如果系統採用的是std::launch::async方式,那麼std::future物件有可能有兩種狀態,因為在新執行緒可能執行完也有可能沒有執行完,此時需要判斷std::future物件狀態是std::future_status::ready還是std::future_status::timeout。

相關文章