命名空間 - this_thread
在C++11中不僅添加了線程類,還添加了一個關于線程的命名空間std::this_thread,在這個命名空間中提供了四個公共的成員函數,通過這些成員函數就可以對當前線程進行相關的操作了。
1.get_id()
調用命名空間std::this_thread中的get_id()方法可以得到當前線程的線程ID,函數原型如下:
thread::id get_id() noexcept;
關于函數使用對應的示例代碼如下:
#include <iostream>
#include <thread>
using namespace std;void func()
{cout << "子線程: " << this_thread::get_id() << endl;
}int main()
{cout << "主線程: " << this_thread::get_id() << endl;thread t(func);t.join();
}
程序啟動,開始執行main()函數,此時只有一個線程也就是主線程。當創建了子線程對象t之后,指定的函數func()會在子線程中執行,這時通過調用this_thread::get_id()就可以得到當前線程的線程ID了。
2.sleep_for()
線程和進程的執行有很多相似之處,在計算機中啟動的多個線程都需要占用CPU資源,但是CPU的個數是有限的并且每個CPU在同一時間點不能同時處理多個任務。為了能夠實現并發處理,多個線程都是分時復用CPU時間片,快速的交替處理各個線程中的任務。因此多個線程之間需要爭搶CPU時間片,搶到了就執行,搶不到則無法執行(因為默認所有的線程優先級都相同,內核也會從中調度,不會出現某個線程永遠搶不到CPU時間片的情況)。
命名空間this_thread中提供了一個休眠函數sleep_for(),調用這個函數的線程會馬上從運行態變成阻塞態并在這種狀態下休眠一定的時長,因為阻塞態的線程已經讓出了CPU資源,代碼也不會被執行,所以線程休眠過程中對CPU來說沒有任何負擔。這個函數是函數原型如下,參數需要指定一個休眠時長,是一個時間段:
template <class Rep, class Period>void sleep_for (const chrono::duration<Rep,Period>& rel_time);
示例代碼:
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;void func()
{for (int i = 0; i < 10; ++i){this_thread::sleep_for(chrono::seconds(1));cout << "子線程: " << this_thread::get_id() << ", i = " << i << endl;}
}int main()
{thread t(func);t.join();
}
在func()函數的for循環中使用了this_thread::sleep_for(chrono::seconds(1));之后,每循環一次程序都會阻塞1秒鐘,也就是說每隔1秒才會進行一次輸出。需要注意的是:程序休眠完成之后,會從阻塞態重新變成就緒態,就緒態的線程需要再次爭搶CPU時間片,搶到之后才會變成運行態,這時候程序才會繼續向下運行。
3.sleep_until()
命名空間this_thread中提供了另一個休眠函數sleep_until(),和sleep_for()不同的是它的參數類型不一樣
- sleep_until():指定線程阻塞到某一個指定的時間點time_point類型,之后解除阻塞
- sleep_for():指定線程阻塞一定的時間長度duration 類型,之后解除阻塞
該函數的函數原型如下:
template <class Clock, class Duration>void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);
示例代碼:
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;void func()
{for (int i = 0; i < 10; ++i){// 獲取當前系統時間點auto now = chrono::system_clock::now();// 時間間隔為2schrono::seconds sec(2);// 當前時間點之后休眠兩秒this_thread::sleep_until(now + sec);cout << "子線程: " << this_thread::get_id() << ", i = " << i << endl;}
}int main()
{thread t(func);t.join();
}
sleep_until()和sleep_for()函數的功能是一樣的,只不過前者是基于時間點去阻塞線程,后者是基于時間段去阻塞線程,項目開發過程中根據實際情況選擇最優的解決方案即可。
4.yield()
命名空間this_thread中提供了一個非常紳士的函數yield(),在線程中調用這個函數之后,處于運行態的線程會主動讓出自己已經搶到的CPU時間片,最終變為就緒態,這樣其它的線程就有更大的概率能夠搶到CPU時間片了。使用這個函數的時候需要注意一點,線程調用了yield()之后會主動放棄CPU資源,但是這個變為就緒態的線程會馬上參與到下一輪CPU的搶奪戰中,不排除它能繼續搶到CPU時間片的情況,這是概率問題。
void yield() noexcept;
函數對應的示例程序如下:
#include <iostream>
using namespace std;
#include <thread>
#include <chrono>void test01() {for (int i = 0; i < 1000; i++) {cout << "子線程" << this_thread::get_id() << ", i = " << i << endl;this_thread::yield();}
}int main() {thread t1(test01);for (int i = 0; i < 1000; ++i) {cout << "主線程" << this_thread::get_id() << ", " << i << endl;}t1.join();return 0;
}
這段程序, 主線程和子線程同時數數到1000, 子線程中調用了yield函數, 會在每次執行一次后出讓線程, 因此主線程大概率會率先數到1000
結論:
- std::this_thread::yield() 的目的是避免一個線程長時間占用CPU資源,從而導致多線程處理性能下降