天天看點

C++多線程:thread類建立線程的多種方式

文章目錄

  • ​​描述​​
  • ​​函數成員簡介​​
  • ​​總結​​

描述

  • 頭檔案 ​

    ​<thread>​

  • 聲明方式:​

    ​std::thread <obj>​

  • 簡介

    線程在構造關聯的線程對象時立即開始執行,從提供給作為構造函數參數的頂層函數開始。如果頂層函數抛出異常,則調用 ​

    ​std::terminate​

    ​。正如我們之前幾篇說過的thread可以通過​

    ​std::async​

    ​和​

    ​std::promise​

    ​類對修改共享變量,并将傳回值傳遞給調用方。如果共享變量在該過程中需要進行同步,則就需要我們說過的​

    ​std::mutex​

    ​和​

    ​std::condition_variable​

    ​。

    ​​

    ​std::thread​

    ​​不支援拷貝構造和指派,如果需要将目前t1 對象指派給t2對象,需要通過移動指派​

    ​std::move​

    ​,即将t1的位址以及内容全部指派給t2對象才行。

函數成員簡介

  • 構造函數(建立線程的多種方式)

    a. 類對象的拷貝建立線程

    b. 類對象的引用建立線程

    c. 移動複制​​

    ​std::move​

    ​​對象建立線程

    d. 臨時建立的對象建立線程

    e. 函數名建立線程

    f. lamda函數建立線程

    g. 類對象的拷貝的成員函數 建立線程

    h. 類對象的位址的成員函數 建立線程

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
 
void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 1 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
class foo
{
public:
    void bar()
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 3 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
class baz
{
public:
    void operator()(int n)
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 4 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
int main()
{
    int n = 0;
    foo f;
    baz b;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // 普通函數名建立線程,傳遞函數參數為值
    std::thread t3(f2, std::ref(n)); // 普通函數名建立線程,傳遞函數參數為引用
    std::thread t4(std::move(t3)); // 類對象的移動指派,使用std::move
    std::thread t5(&foo::bar, &f); // 類對象的拷貝的位址的成員函數
    std::thread t6(b,6); // 類對象建立線程,預設執行operator ()函數
    std::thread t7([](int x){return x*x;},6);//lamda函數建立子線程
    std::thread t8(std::ref(b),6);//類對象的引用建立
    std::thread t9(baz(),6);//臨時對象建立
    
    t2.join();
    t4.join();
    t5.join();
    t6.join();
    t7.join();
    t8.join();
    t9.join();
    std::cout << "Final value of n is " << n << '\n';
    std::cout << "Final value of foo::n is " << f.n << '\n';
}      
  • 輸出如下,由于輸出為線程間交叉執行,輸出會有點亂:
Thread 2 executing
Thread 4 executing
Thread 1 executing
Thread 3 executing
Thread 4 executing
Thread 4 executing
Thread 2 executing
Thread 1 executing
Thread 3 executing
Thread 4 executing
Thread 4 executing
Thread 4 executing
Thread 2 executing
Thread 1 executing
Thread 4 executing
Thread 3 executing
Thread 4 executing
Thread 4 executing
Thread 2 executing
Thread 4 executing
Thread 4 executing
Thread 1 executing
Thread 3 executing
Thread 4 executing
Thread 2 executing
Thread 4 executing
Thread 4 executing
Thread 3 executing
Thread 4 executing
Thread 1 executing
Final value of n is 5
Final value of foo::n is 5      
  • ​std::thread::joinable​

    ​​檢查 thread 對象是否辨別活躍的執行線程,是傳回true,否則傳回false

    如下代碼

#include <iostream>
#include <thread>
#include <chrono>
 
void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
int main()
{
    //剛建立好的線程對象是不活躍的
    std::thread t;
    std::cout << "before starting, joinable: " << std::boolalpha << t.joinable()
              << '\n';
    //建立線程并執行代碼,但未合并,則它是活躍的
    t = std::thread(foo);
    std::cout << "after starting, joinable: " << t.joinable() 
              << '\n';
 
    //線程合并之後變為不活躍的
    t.join();
    std::cout << "after joining, joinable: " << t.joinable() 
              << '\n';
}      
  • 輸出如下:
before starting, joinable: false
after starting, joinable: true
after joining, joinable: false      
  • ​std::thread::join​

    ​​阻塞線程成員,直到線程函數代碼執行完畢傳回

    代碼如下:

#include <iostream>
#include <thread>
#include <chrono>
 
void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
void bar()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
int main()
{
    std::cout << "starting first helper...\n";
    std::thread helper1(foo);
 
    std::cout << "starting second helper...\n";
    std::thread helper2(bar);
 
    std::cout << "waiting for helpers to finish..." << std::endl;
    helper1.join();
    helper2.join();
 
    std::cout << "done!\n";
}      
  • 輸出如下:
starting first helper...
starting second helper...
waiting for helpers to finish...
done!      
  • ​std::thread::detach​

    ​​從 thread 對象分離執行的線程,允許執行獨立地執行線程

    一旦線程退出,則釋放所有配置設定的資源。

    如下代碼:

#include <iostream>
#include <chrono>
#include <thread>
 
void independentThread() 
{
    std::cout << "Starting concurrent thread.\n";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Exiting concurrent thread.\n";
}
 
void threadCaller() 
{
    std::cout << "Starting thread caller.\n";
    std::thread t(independentThread);
    //這裡調用detach,則線程t将獨立執行,是以目前線程的輸出會早于t線程的輸出
    t.detach();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Exiting thread caller.\n";
}
 
int main() 
{
    threadCaller();
    std::this_thread::sleep_for(std::chrono::seconds(5));
}      
  • 輸出如下:
Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
Exiting concurrent thread.      
  • ​std::thread::swap​

    ​​交換兩個線程的對象的檔案句柄

    代碼如下:

#include <iostream>
#include <thread>
#include <chrono>
 
void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
void bar()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
int main()
{
    std::thread t1(foo);
    std::thread t2(bar);
 
    std::cout << "thread 1 id: " << t1.get_id() << std::endl;
    std::cout << "thread 2 id: " << t2.get_id() << std::endl;
 
    使用boost的swap和thread類封裝的swap函數效果類似,都會交換線程的檔案句柄
    std::swap(t1, t2);
 
    std::cout << "after std::swap(t1, t2):" << std::endl;
    std::cout << "thread 1 id: " << t1.get_id() << std::endl;
    std::cout << "thread 2 id: " << t2.get_id() << std::endl;
 
    t1.swap(t2);
 
    std::cout << "after t1.swap(t2):" << std::endl;
    std::cout << "thread 1 id: " << t1.get_id() << std::endl;
    std::cout << "thread 2 id: " << t2.get_id() << std::endl;
 
    t1.join();
    t2.join();
}      
  • 輸出如下:
thread 1 id: 0x70000f1e9000
thread 2 id: 0x70000f26c000
after std::swap(t1, t2):
thread 1 id: 0x70000f26c000
thread 2 id: 0x70000f1e9000
after t1.swap(t2):
thread 1 id: 0x70000f1e9000
thread 2 id: 0x70000f26c000      

總結