风险代码
#include <iostream>
#include <thread>
void do_something(unsigned int i){
printf("%d\n", i);
}
struct func
{
int& i;
func(int& i_) : i(i_) {}
void operator() ()
{
for (unsigned j = 0; j<10; ++j)
{
do_something(i); // 潜在访问隐患:悬空引用
}
}
};
class thread_guard
{
std::thread& t;
public:
// explicit关键字只能用于修饰只有一个参数的类构造函数
// 防止std::thread&隐式转换成thread_guard&
explicit thread_guard(std::thread& t_):t(t_){}
~thread_guard()
{
std::cout << "delete thread_guard\n";
if (t.joinable()) // join()只能对给定的对象调用一次, 已经join的线程不能再次join
{
t.join();
}
}
thread_guard(thread_guard const&) = delete; // 禁止拷贝构造函数,风险:弄丢已经加入的线程
thread_guard& operator=(thread_guard const&) = delete; // 禁止赋值操作 风险:弄丢已经加入的线程
};
void do_something_in_current_thread() {
throw "do_something_in_current_thread error!!";
}
int main()
{
int value = 0;
// 1.my_func引用value变量
func my_func(value);
// 2.启动线程
std::thread t(my_func);
// 3.抛出异常,直接跳到程序结束位置
do_something_in_current_thread();
t.join();
return 0;
// 4.程序结束释放value,此时t没有结束继续引用value导致访问非法内存
}
使用RAII
void do_something(unsigned int i){
printf("%d\n", i);
}
struct func
{
int& i;
func(int& i_) : i(i_) {}
void operator() ()
{
for (unsigned j = 0; j<10; ++j)
{
do_something(i); // 1. 潜在访问隐患:悬空引用
}
}
};
class thread_guard
{
std::thread& t;
public:
// explicit关键字只能用于修饰只有一个参数的类构造函数
// 防止std::thread&隐式转换成thread_guard&
explicit thread_guard(std::thread& t_):t(t_){}
~thread_guard()
{
std::cout << "delete thread_guard\n";
if (t.joinable()) // join()只能对给定的对象调用一次, 已经join的线程不能再次join
{
// 5.等待线程结束,此时some_local_state没有被释放
t.join();
}
}
thread_guard(thread_guard const&) = delete; // 禁止拷贝构造函数,风险:弄丢已经加入的线程
thread_guard& operator=(thread_guard const&) = delete; // 禁止赋值操作 风险:弄丢已经加入的线程
};
void do_something_in_current_thread() {
throw "do_something_in_current_thread error!!";
}
int main()
{
int some_local_state = 0;
// 1.传引用
func my_func(some_local_state);
std::thread t(my_func);
// 2.利用RAII 防止程序销毁some_local_state时t线程还在引用some_local_state
thread_guard g(t);
// 3.do_something_in_current_thread抛出异常, 直接跳到程序结束位置
do_something_in_current_thread();
return 0;
} // 4.资源逆向销毁,先析构g对象,后释放some_local_state
// 6.程序结束
存一个知乎大佬的RAII讲解