C++ 并发 --- 1. [线程使用]

121 阅读2分钟

1. 发起线程

C++ thread支持 普通函数、lambda、可调用对象,作为线程启动执行的任务
创建完thread对象后,必须调用join()或者detach()函数,为什么后面源码会讲到

1.1 普通函数

#include <thread>
#include <stido.h>
using namespace std;

void func()
{
    printf("func()\n");
}

int main()
{
/////////////////////// 接收普通函数
    thread t(func);   
    t.join();   // 需要调用join或detach,否则thread析构的时候调用terminate()则崩溃
///////////////////////
    return 0;
}

1.2 普通函数,传入参数

使用std::bind将函数指针和参数绑定然后传入给thread对象

// 省略掉头文件...

void func(vector<int>& vec)
{
    for (auto item : vec)
        cout << item << " ";
}

int main()
{
    vector<int> vec{ 1, 2, 3, 4, 5};
    ///////////////////// 使用std::bind 
    thread t(std::bind(func, vec));
    /////////////////////
    t.join();
    return 0;
}

1.3 lambda

int main()
{
    thread t([](){
        cout << "lambda func()" << endl;
        });
   t.join();
   
   return 0;
}

1.4 可调用对象

// 省略头文件 ....
struct A
{
    void operator()()
    { 
        cout << "operator()()" << endl;
    }
};

int main()
{
    thread t(A());    
    t.join();    // 这里报错
    return 0;
}

上面t.join()报错,是因为 thread t(A()); 把这一行当作了函数声明. 解决方法是:
thread t( (A()) ); 在A()再套上一层小括号。
thread t { A() }; 或者是这样,列表初始化

2. MSVC的thread源码

thread UML类图 image.png

2.1 thread构造来创建线程

通过下面两张图可知。创建thread对象的时候,传入函数和参数作为线程执行的任务。如果函数的参数是左值,那么当我们传入一个参数的时候,会报错Invoke不匹配。这就是因为thread内部去掉了参数的引用。 image.png

image.png

2.2 join()和detach()函数

image.png

2.3 thread的析构

之前说过,创建完thread对象必须调用detach()或者join(), 否则会发生异常

image.png

所以根据上面源码得知, 一个线程对象join()或者detach()会把成员变量_Thr置为空。那么一个线程执行完,就可以再次给这个对象重新赋值,如下:

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

int main()
{
    thread t(func);
    t.join();	

    // 第一个线程已经执行完
    t = thread(func);	// 给t对象重新赋值一个线程对象
    t.join();
    return 0;
}