发起线程

发起一个空白线程

std::thread t;

将lambda函数作为参数

std::thread t([](){do_something();});

auto f = [](){do_something();};
std::thread t(f);

将类成员函数作为参数

class A{
    private:
        int a = 0;
    public:
        void add(){std::cout << a + 1;}    
};
A a = A();
std::thread t(&A::add, &a);

向线程传入参数

auto f = [](int a, int b){std::cout << a+b;};
std::thread t(f,a,b);
  • 线程一旦启动,就必须明确是等待它结束,还是让其独自运行
  • 如果std::thread对象销毁时未明确方式,std::thread的析构函数会调用std::terminate()终止整个程序
  • 对于可调用函数对象,传入临时变量时编译器可能将其解释为函数声明,如std::thread(f()),可以通过多用一对圆括号,或者是列表初始化方式解决

等待线程结束

std::thread t(do_something);
t.join(); //等待线程结束

分离线程

std::thread t(do_something);
t.detach(); //不等待线程结束
  • 如果新线程被分离, 该线程可能会使用到已经被销毁的局部变量,处理方法通常为将需要的数据复制到线程内部,而不是共享数据
  • join()和detach()对一个线程只能调用一次,一个线程一旦选择等待,就不能分离,一个线程,一旦分离,就与主线程分离,不能选择继续汇合,线程的joinable()成员函数可以用于线程是否能够汇合
  • 如果线程启动以后有异常抛出,而join()还未被执行,则join()调用会被略过
  • 如果必须保证新线程先行结束,可以设计一个类,通过标准RAII手法,在析构函数中调用join(),保证主线程在按顺序析构时等待线程的汇合
class thread_guard
{
private:
    std::thread t_;
public:
    explicit thread_guard(thread t):t_(std::move(t)){}
    ~thread_guard(){
        if(t_.joinable()) t_.join();
    }
};
void f(){
    int data = 0;
    auto func = [](int num){do_somthing();};
    thread t(f,data);
    thread_guard g(t);
}
  • 试图向线程所执行的函数中传入非const引用会导致编译无法通过,原因是线程不清楚所需执行的函数其参数类型,而是直接复制所提供的值,如果需要按照引用方式传递参数,可以使用std::ref函数对引用进行包装
int num = 0;
auto func = [](int& num){do_something();};

// 编译失败,函数func期待一个引用类型,却获取到了一个右值
std::thread t(func,num); 
// 通过std::ref包装确保传入引用类型
std::thread t(func,std::ref(num));
  • 如果数据只需要在线程中使用,可以通过std::move向线程转移数据的归属权
int data = 0;
auto f = [](int&& data){do_something();};
thread t(f,std::move(data));

转移线程归属权

  • std::thread对象负责管控一个执行线程,可以移动,不能复制,可以使用std::move在对象之间转移std::thread对象的归属权

std::thread t1(some_function);
std::thread t2(std::move(t1));
  • 线程被分离或者被汇合之后,则不能继续转移

在运行时选择线程数量

  • std::thread::hardware_concurrency()用于获取程序中可以真正并发的线程数量,
//若信息无法获取,函数返回0
const size_t concurrency_num = std::thread::hardware_concurrency();
const size_t count = concurrency_num ? concurrency_num : 2;

识别线程

  • 通过get_id()获取线程ID
std::cout << std::this_thread::get_id();

发表评论