1. 多執行緒程式設計
在進行桌面應用程式開發的時候, 假設應用程式在某些情況下需要處理比較複雜的邏輯, 如果只有一個執行緒去處理,就會導致視窗卡頓,無法處理使用者的相關操作。這種情況下就需要使用多執行緒,其中一個執行緒處理視窗事件,其他執行緒進行邏輯運算,多個執行緒各司其職,不僅可以提高使用者體驗還可以提升程式的執行效率。
如果要使用Qt的多執行緒程式設計的話,可以看這篇
這篇我重點記錄一下C++11中的多執行緒程式設計方法
2. std::thread
C++11中加入了標頭檔案,此標頭檔案主要聲明瞭std::thread執行緒類。C++11的標準類std::thread對執行緒進行了封裝,定義了C++11標準中的一些表示執行緒的類、用於互斥訪問的類與方法等。應用C++11中的std::thread便於多執行緒程式的移值。
std::thread類成員函式:
(1)、get_id:獲取執行緒ID,返回一個型別為std::id的物件。
(2)、joinable:檢查執行緒是否可被join。檢查thread物件是否標識一個活動(active)的可行性執行緒。預設構造的thread物件、已經完成join的thread物件、已經detach的thread物件都不是joinable。
(3)、join:呼叫該函式會阻塞當前執行緒。阻塞呼叫者(caller)所在的執行緒直至被join的std::thread物件標識的執行緒執行結束。
(4)、detach:將當前執行緒物件所代表的執行例項與該執行緒物件分離,使得執行緒的執行可以單獨進行。一旦執行緒執行完畢,它所分配的資源將會被釋放。
(5)、native_handle:該函式返回與std::thread具體實現相關的執行緒控制代碼。native_handle_type是連線thread類和作業系統SDK API之間的橋樑,如在Linux g++(libstdc++)裡,native_handle_type其實就是pthread裡面的pthread_t型別,當thread類的功能不能滿足我們的要求的時候(比如改變某個執行緒的優先順序),可以通過thread類例項的native_handle()返回值作為引數來呼叫相關的pthread函式達到目錄。This member function is only present in class thread if the library implementation supports it. If present, it returns a value used to access implementation-specific information associated to the thread.
(6)、swap:交換兩個執行緒物件所代表的底層控制代碼。
(7)、operator=:moves the thread object
(8)、hardware_concurrency:靜態成員函式,返回當前計算機最大的硬體併發執行緒數目。基本上可以視為處理器的核心數目。
另外,std::id表示執行緒ID,定義了在執行時作業系統內唯一能夠標識該執行緒的識別符號,同時其值還能指示所標識的執行緒的狀態。Values of this type are returned by thread::get_id and this_thread::get_id to identify threads.
有時候我們需要線上程執行程式碼裡面對當前呼叫者執行緒進行操作,針對這種情況,C++11裡面專門定義了一個名稱空間this_thread,此名稱空間也宣告在標頭檔案中,其中包括get_id()函式用來獲取當前呼叫者執行緒的ID;yield()函式可以用來將呼叫者執行緒跳出執行狀態,重新交給作業系統進行排程,即當前執行緒放棄執行,作業系統排程另一執行緒繼續執行;sleep_until()函式是將執行緒休眠至某個指定的時刻(time point),該執行緒才被重新喚醒;sleep_for()函式是將執行緒休眠某個指定的時間片(time span),該執行緒才被重新喚醒,不過由於執行緒排程等原因,實際休眠實際可能比sleep_duration所表示的時間片更長。
2.1 建立一個執行緒
直接使用std::thread 來例項化
#include <iostream>
#include <thread>
#include <stdlib.h> //sleep
using namespace std;
void t1()
{
for (int i = 0; i < 10; ++i)
{
cout << "部落格園-進擊的汪sir\n";
sleep(1);
}
}
void t2()
{
for (int i = 0; i < 20; ++i)
{
cout << "1111111111111\n";
sleep(1);
}
}
int main()
{
thread th1(t1); //例項化一個執行緒物件th1,使用函式t1構造,然後該執行緒就開始執行了(t1())
thread th2(t2);
th1.join(); // 必須將執行緒join或者detach 等待子執行緒結束主程序才可以退出
th2.join();
//or use detach
//th1.detach();
//th2.detach();
cout << "here is main\n\n";
return 0;
}
2.2 lambda表示式
用lambda表示式來執行多執行緒非常的方便,適合處理一些比較小的任務
std::thread t3([](int a, double b){cout << a << ' ' << b << endl;}, 3, 4);
3. join與detach的區別
每一個程式至少擁有一個執行緒,那就是執行main()函式的主執行緒,而多執行緒則是出現兩個或兩個以上的執行緒並行執行,即主執行緒和子執行緒在同一時間段同時執行。而在這個過程中會出現幾種情況:
主執行緒先執行結束
子執行緒先執行結束
主子執行緒同時結束
在一些情況下需要在子執行緒結束後主執行緒才能結束,而一些情況則不需要等待,但需注意一點,並不是主執行緒結束了其他子執行緒就立即停止,其他子執行緒會進入後臺執行
3.1 join()
join()函式是一個等待執行緒完成函式,主執行緒需要等待子執行緒執行結束了才可以結束
#include <iostream>
#include "thread"
using namespace std;
void func()
{
for(int i = 0; i <3; i++)
{
cout << "from func():" << i << endl;
}
}
int main() {
std::cout << "Hello, World!" << std::endl;
std::cout << "Hello, World!" << std::endl;
thread t1(func);
thread::id t1_id = t1.get_id();
thread::id this_id = this_thread::get_id();
t1.join();
std::cout << "t1_id:" << t1_id << endl;
std::cout << "this_id: " << this_id <<endl;
return 0;
}
執行結果
3.2 detach()
稱為分離執行緒函式,使用detach()函式會讓執行緒在後臺執行,即說明主執行緒不會等待子執行緒執行結束才結束
通常稱分離執行緒為守護執行緒(daemon threads),UNIX中守護執行緒是指,沒有任何顯式的使用者介面,並在後臺執行的執行緒。這種執行緒的特點就是長時間執行;執行緒的生命週期可能會從某一個應用起始到結束,可能會在後臺監視檔案系統,還有可能對快取進行清理,亦或對資料結構進行優化。另一方面,分離執行緒的另一方面只能確定執行緒什麼時候結束,發後即忘(fire andforget)的任務就使用到執行緒的這種方式
#include <iostream>
#include "thread"
using namespace std;
void func()
{
for(int i = 0; i <3; i++)
{
cout << "from func():" << i << endl;
}
}
int main() {
thread t1(func);
thread::id t1_id = t1.get_id();
std::cout << "Hello, World!" << std::endl;
thread::id this_id = this_thread::get_id();
t1.detach();
std::cout << "Hello, World!" << std::endl;
std::cout << "t1_id:" << t1_id << endl;
std::cout << "this_id: " << this_id <<endl;
return 0;
}
執行結果
4. 參考
https://blog.csdn.net/sevenjoin/article/details/82187127