參考資料
線程的創建
線程的創建有多種方式
std::thread t1(可調用對象);
由於實現(內部的實現這里不在探討),std::thread()創建一個新的線程可以接受任意的可調用對象類型(帶參數或者不帶參數),包括lambda表達式(帶變量捕獲或者不帶),函數,函數對象,以及函數指針。
下面簡單的探討一下。
1.通過一個不帶參數的函數創建線程。
#include <iostream>
#include <thread> // 多線程頭文件
void Hello() {
std::cout << "Hello, World!" << std::endl;
}
int main() {
// 創建一個線程對象,注意函數 Hello 將立即運行。
std::thread t(&Hello);
// 等待線程結束。
// 否則線程還沒執行(完),主程序就已經結束了。
t.join();
return 0;
}
在啟動了一個線程(創建了一個thread對象)之后,當這個線程結束的時候(std::terminate()),我們如何去回收線程所使用的資源呢?
thread庫給我們兩種選擇:
- 1.加入式(join())
- 2.分離式(detach())
值得一提的是,你必須在thread對象銷毀之前做出選擇 這是因為線程可能在你加入或分離線程之前,就已經結束了,之后如果再去分離它,線程可能會在thread對象銷毀之后繼續運行下去。
2.通過一個帶參數的函數創建線程。
#include <iostream>
#include <thread>
void Hello(const char* what) {
// 睡眠一秒以模擬數據處理。
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Hello, " << what << "!" << std::endl;
}
int main() {
std::thread t(&Hello, "World");
// 等價於使用 bind:
// std::thread t(std::bind(&Hello, "World"));
t.join();
return 0;
}
3.通過一個函數對象——即仿函數(functor)——創建線程。
class Hello {
public:
void operator()(const char* what) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Hello, " << what << "!" << std::endl;
}
};
int main() {
Hello hello;
// 方式一:拷貝函數對象。
std::thread t1(hello, "World");
t1.join();
// 方式二:不拷貝函數對象,通過 boost::ref 傳入引用。
// 用戶必須保證被線程引用的函數對象,擁有超出線程的生命期。
// 比如這里通過 join 線程保證了這一點。
std::thread t2(std::ref(hello), "World");
t2.
return 0;
}
4.通過一個成員函數創建線程。
// 通過成員函數來創建線程. 注意和普通函數的區別
#include <thread>
#include <iostream>
class Hello {
public:
void ThreadFuntion() {
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "ThreadFuntion1 " << std::this_thread::get_id() << std::endl;
}
};
int main() {
Hello hello; // 需要一個對象
std::thread t1(&Hello::ThreadFuntion, &hello);
t1.join();
return 0;
}
5.通過一個成員函數創建線程(在構造函數中操作)。
與前例不同之處在於,需要以 bind 綁定 this 指針作為第一個參數。
#include <iostream>
#include <thread>
class Hello {
public:
Hello() {
std::thread t(std::bind(&Hello::Entry, this, "World"));
t.join();
}
private:
// 線程函數
void Entry(const char* what) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Hello, " << what << "!" << std::endl;
}
};
int main() {
Hello hello;
return 0;
}
6.使用lambda
#include <iostream>
#include <thread>
//
int main() {
auto threadFunction = []() {
std::cout << "Lambda thread id " << std::this_thread::get_id() <<std::endl; // 打印線程id
std::cout << "使用lambda表達式作為可調用對象" << std::endl;
};
std::cout << "Main thread id " << std::this_thread::get_id() <<std::endl;
std::thread t1(threadFunction);
t1.join();
return 0;
}
join Vs detach
join
join()字面意思是連接一個線程,意味着主動地等待線程的終止。
join()是這樣工作的,在調用進程中join(),當新的線程終止時,join()會清理相關的資源(any storage associated with the thread),然后返回,調用線程再繼續向下執行。 正是由於join()清理了線程的相關資源,因而我們之前的thread對象與已銷毀的線程就沒有關系了,這意味着一個線程的對象每次你只能使用一次join(),當你調用的join()之后joinable()就將返回false了。
關於joinable()
判斷是否可以成功使用join() 或者detach(), 返回值為bool .
#include <iostream>
#include <thread>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t(foo);
std::cout << "before joining,joinable=" << std::boolalpha << t.joinable() << std::endl;
t.join();
std::cout << "after joining, joinable=" << std::boolalpha << t.joinable() << '\n';
}
// output
// [thread]main
// before joining,joinable=true
// after joining, joinable=false
detach
分離式,對應的函數是detach()。
detach這個詞的意思是分離的意思,對一個thread對象使用detach()意味着從調用線程分理出這個新的線程,我們稱分離的線程叫做守護線程(daemon threads)。之后也就不能再與這個線程交互。
分離的線程會在后台運行,其所有權(ownership)和控制權將會交給c++運行庫。同時,C++運行庫保證,當線程退出時,其相關資源的能夠正確的回收。
分離的線程,它運行結束后,不再需要通知調用它的線程
線程的標識
線程id。
類 thread::id 是輕量的可頻繁復制類,它作為 std::thread 對象的唯一標識符工作。
- std::this_thread::get_id()這個函數獲取線程的標識符 (在線程的函數中)
- 線程對象也有一個function get_id()
std::thread::id get_id() const noexcept;
// source: cppreference
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
// 看這里
std::cout << std::this_thread::get_id() <<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
// 看這里
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
t1.join();
t2.join();
}