C++ 创建线程

237 阅读2分钟

C++ 创建线程的常见方式

C++ 创建线程需要 #include<thread>

1. 普通函数创建

#include <iostream>
#include <thread>

void func()
{
    std::cout << "func" << std::endl;
}

int main()

{
    std::thread thread(func);

    // thread.join();  // join 方法会等到线程执行完毕在继续执行 thread.join 之后的内容

    thread.detach();    // detach 线程分离方法,主线程和子线程分离,不会阻塞

    // sleep(2);    // 如果 func 线程种需要运行很长时间,并采取了detach 方法,当主线程

                    // 过早结束,子线程会终止,所以需要确保主线程在子线程结束之后再结束
}

2. lambda 函数创建

    int a = 10;
    
    std::thread t([a](int b){
        std::cout << a << std::endl;
        std::cout << b << std::endl;
        std::cout << "lambda" << std::endl;
    }, 100);

3. 一个类创建一个线程

  • 在使用类创建线程的时候需要注意的是,线程中对类的拷贝次数

#include <iostream>
#include <thread>
#include <unistd.h>

class back_ground
{
public:
    int id;
    back_ground(int i): id(i)
    {
        std::cout << "back_ground()" <<std::endl;
    }
    
    back_ground(const back_ground& b)
    {
        std::cout << "&back_ground()" << std::endl;
        id = b.id + 10;
    }

    void operator()() const     // 符号重载 ()
    {
        std::cout << "operator()(), id: " << id << std::endl;
    }

    void do_something()
    {
        std::cout << "do_something(), id: " << id << std::endl;
    }

    ~back_ground()
    {
        std::cout << "~back_ground(), id: " << id  << std::endl;
    }
};

int main()

{

    back_ground b(3);

    // 1. 构造std::thread对象时:如果不带参则会创建一个空的thread对象,但底层线程并没有真正被创建,一般可将其他std::thread对象通过move移入其中;如果带参则会创建新线程,而且会被立即运行。

    // 2. 在创建thread对象时,std::thread构建函数中的所有参数均会按值并以副本的形式保存成一个tuple对象。该tuple由调用线程(一般是主线程)在堆上创建,并交由子线程管理,在子线程结束时同时被释放。

    // 3. std:thread t1(b) 调用拷贝构造函数2次, t1(b) 调用一次拷贝构造, std::thread在构造时,建立tuple对象,tuple对象内部_Tuple_val进行了Ta对象的拷贝

    // 不同的编译器有不同的实现,gcc中会拷贝两次,msvc只拷贝一次

    std::thread t1(b);  

    // std::thread t1(&back_ground::do_something, b);   // 和 std::thread t1(b) 一样执行两次构造

    t1.join();

    std::cout << "---------" << std::endl;

}


/* out:

back_ground()                   // 1. back_ground b(3) 构造函数, id = 3

&back_ground()                  // 2. t1(b) 第一次调用拷贝构造,id = 3 + 10  = 13

&back_ground()                  // 3. t2(b) 第二次调用拷贝构造,id = 13 + 10 = 23

~back_ground(), id: 13          // 4. 析构第一次调用拷贝构造的 back_ground 类

operator()(), id: 23            // 5. 执行函数重载的函数

~back_ground(), id: 23          // 6. 析构第二次调用拷贝构造的 back_ground 类

---------                       // 7. t.join,线程执行完毕,回到main函数

~back_ground()3                 // 7. 最初的构造的类的析构

*/

  


Tips

获取当前硬件的核心数


std::thread::hardware_concurrency()     // 获取cpu的核心数,获取不到的时候返回0

获取线程id


std::thread::get_id()           // 线程类获取线程 id

std::this_thread::get_id()      // 在线程内部获取线程 id