C++11多線程編程--線程創建


參考資料

線程的創建

線程的創建有多種方式

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;
}

cppreference 關於立即運行有提到

在啟動了一個線程(創建了一個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();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM