一、启创建线程
std::thread接受可调用对象创建线程
1. 使用函数作为线程的执行体
void myFunction()
{
}
std::thread t1(myFunction);
//带有参数的函数调用:
void myFunction2(int&& a)
{
std::cout << "a的值为:" << a << std::endl;
}
int i = 10;
std::thread t2(myFunction2, std::move(i));
thread在传递参数时,是以右值传递的:
template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args)
如果想要传入左值呢?
使用函数作为线程的执行体时,若传入参数为左值引用,需要使用std::ref。
如果不使用std::ref,而是直接将 a 传递给 std::thread,则会发生参数类型的改变,导致编译错误,这是因为 std::thread 的构造函数会复制参数,并且复制引用类型的参数会导致编译错误。
std::ref 是一个包装器,它会将参数转换为引用类型,以便在std::thread的构造函数中正确传递引用参数,避免在将参数传递给模板函数或函数对象时丢失参数的引用性质
void myFunction()
{
}
std::thread t1(myFunction);
//带有参数的函数调用:
void myFunction2(int& a)
{
std::cout << "a的值为:" << a << std::endl;
}
int i = 10;
std::thread t2(myFunction2, std::ref(i));
2. 使用类的成员函数作为线程的执行体
class TA
{
public:
TA(int &&i):m_i(i)
{
cout << "TA()构造函数被执行" << endl;
}
TA(const TA &ta) :m_i(ta.m_i)
{
cout << "TA()拷贝构造函数被执行" << endl;
}
~TA()
{
cout << "~TA()析构函数被执行" << endl;
}
//仿函数,使用重载函数operator()
void operator()()
{
cout << "m_i的值为:" << m_i << endl;
}
void myMethod(int &i) {
cout << "myMethod中m_i的值为:" << m_i << endl;
cout << "myMethod中i的值为:" << i << endl;
}
private:
int m_i;
};
int main()
{
TA obj(10);
thread mythread(obj); //调用拷贝构造函数,实际上是复制到线程中去的
int paramValue = 42;
//带有参数的线程函数调用
thread myta(&TA::myMethod, &obj, ref(paramValue));
//ref:将传入的对象包装为引用类型的包装器,并提供一个类似引用的接口。
//这样可以在使用包装器时模拟引用传递的效果。
myta.join();
mythread.join();
return 0;
}
3. 使用lmbda表达式作为线程的执行体
- 无参数函数:
void myMethod()
{
cout << "开启myMethod函数线程" << endl;
}
int main()
{
std::thread t1([]() {
myMethod();
});
t1.join();
return 0;
}
- 带参数函数
void myMethod(int& a)
{
cout << "a的值为:" << a << endl;
}
int main()
{
int a = 10;
std::thread t1([&]() {
myMethod(a);
});
t1.join();
return 0;
}
- 调用仿函数
class TA
{
public:
TA(int &i):m_i(i){}
void operator()()
{
cout << "m_i的值为:" << m_i << endl;
}
private:
int m_i;
};
int main()
{
int value = 10;
std::thread t1([&]() {
TA obj1(value);
obj1();
});
t1.join();
return 0;
}
- 调用类成员函数
class TA
{
public:
TA(int &i):m_i(i){}
void operator()()
{
cout << "m_i的值为:" << m_i << endl;
}
void myMethod(int &m) {
cout << "myMethod中m_i的值为:" << m_i << endl;
cout << "myMethod中m的值为:" << m << endl;
}
private:
int m_i;
};
int main()
{
int value = 10;
int paramValue = 40;
TA obj(value);
std::thread t1([&]() {
obj.myMethod(paramValue);
});
t1.join();
return 0;
}
二、join()和detach()
std::thread 类提供了 join() 和 detach() 两个成员函数,用于控制线程的行为。
1. join()
join()函数用于等待线程的完成。- 调用
join()会阻塞当前线程,直到被调用的线程执行完毕。也就是说,join()会将主线程挂起,直到被调用的线程执行完毕,主线程才会继续执行后面的代码。 - 使用
join()可以保证在主线程退出之前,子线程的执行完毕,避免了子线程被强制终止。
2. detach()
detach()函数用于分离线程,使得目标线程成为守护线程。调用detach()后,被调用的线程与主线程分离,它们可以并发执行。分离的线程在执行结束后会自动清理资源,不需要主线程进行等待。- 一旦线程被分离,无法再与之通信,无法再使用
join()等待其完成。
3. 必须使用 join() 或 detach() 进行处理
对于一个 std::thread 对象,必须使用 join() 或 detach() 进行处理,否则在 std::thread 对象析构时会触发 std::terminate() 终止程序的执行。这是因为 std::thread 对象在析构时要检查线程的状态,如果线程还在运行且未被 join() 或 detach() 处理,那么就会引发终止。
4. joinable()
joinable()是std::thread类的成员函数,用于检查线程是否可以被join()或detach()。它返回一个布尔值,指示线程是否可被加入(joinable)。
- 如果
joinable()返回 true,表示线程可以被 join() 或 detach(),即线程处于活动状态,尚未被加入或分离。 - 如果
joinable()返回 false,表示线程不可被 join() 或detach(),即线程已经被加入或分离,或者是默认构造的空线程对象。
使用 joinable() 函数在编程中进行线程状态的检查,以避免对不可被加入的线程进行错误的操作。通常在使用 join() 或 detach() 之前,都会先检查线程是否可被加入,以确保正确处理线程对象。
std::thread t(myFunction); // 创建线程
if (t.joinable()) {
t.join(); // 加入线程
}
5. 使用detach()注意事项
- 若传递
int这种简单类型参数,建议都是值传递,不要引用,防止局部变量失效导致线程对内存的非法引用 - 若传递类对象,避免隐式类型转换,全部都在创建线程传参时就构建出临时对象来,然后在函数参数里用引用来接,否则系统会再多构造一次对象。
- 建议不适用detach()
本文详细介绍了如何使用std::thread创建线程,包括使用函数、类成员函数以及lambda表达式作为线程执行体,并讨论了参数传递、join()和detach()的使用,强调了线程安全和资源管理的重要性。
&spm=1001.2101.3001.5002&articleId=131455861&d=1&t=3&u=c2e8fb7469434df4b3a06db4d89336ff)
2153

被折叠的 条评论
为什么被折叠?



