keywords
- std::enable_shared_from_this
- 线程安全
- std::weak_ptr
0 引言
C++11的一些高级性能,方便了我们的编程,但是面对这些高级特性的同时,也给我们埋了很多坑。比如,看下面这段代码:
#include <iostream>
#include <memory>
#include <unistd.h>
class A {
public:
A(int val):val_(val){}
std::shared_ptr<A> GetPtr(){
return std::shared_ptr<A>(this);
}
private:
int val_;
};
int main(){
auto a = std::make_shared<A>(1);
auto b = a->GetPtr();
return 0;
}
不知道你是否看出这段代码的问题? 我大概解释下:使用std::shared_ptr的时候,有一个注意事项一定一定要避免不同的ptr指向同一个对象
这个原则,大家想想也知道,好比,一台车卖给了2个人,那这个车到底归谁呢? 另外,比喻恰当点:当两个人都想卖这台车额时候,第一个人如果卖了,第二个人拿啥卖呢?
1 方案
有问题当然需要解决
1.1 enable_shared_from_this
但是我们很难避免需要把this指针当做std::shared_ptr参数传递的场景,怎么办呢?
C++当然针对这个问题,是有方案的,那就是使用 std::enable_shared_from_this,上面代码修改如下:
#include <iostream>
#include <memory>
#include <unistd.h>
class A: public std::enable_shared_from_this<A>{
public:
A(int val):val_(val){}
std::shared_ptr<A> GetPtr(){
return shared_from_this();
}
private:
int val_;
};
int main(){
auto a = std::make_shared<A>(1);
auto b = a->GetPtr();
return 0;
}
到这里,关于将this指针当做参数传递的事情是解决了,但是接下来,继续看。
1.2 enable_shared_from_this 使用之坑
1.2.1 与构造函数之坑
禁止在构造函数中,使用。这个其实也好理解:对象都没构造完,哪里来的对象让你shared呢
1.2.2 与析构函数之坑
注意,在进入到析构函数之前,shared_ptr的reference count已经是0了,此时不能在访问之前shared_from_this()了
1.2.3 异步访问之坑
我们可能会有需求,将拿到的shared_ptr对象交给异步的线程来使用,此时,比如,看看下面代码:
#include <iostream>
#include <memory>
#include <unistd.h>
#include <thread>
#include <atomic>
struct A : public std::enable_shared_from_this<A> {
A() {
inited_.store(true);
}
bool isInit() {
return inited_.load();
}
void debug() {
std::cout << "debug" << std::endl;
}
void init() {
std::shared_ptr<A> ptr = shared_from_this();
thread_ = std::thread([ptr](){
usleep(200*10000);
ptr->debug();
});
}
~A() {
std::cout << "pre ~A" << std::endl;
usleep(1000 * 1000);
inited_.store(false);
std::cout << "wait ~A" << std::endl;
if (thread_.joinable()) {
thread_.join();
}
std::cout << "post ~A" << std::endl;
}
private:
std::atomic<bool> inited_;
std::thread thread_;
};
int main() {
{
auto a = std::make_shared<A>();
a->init();
usleep(100 * 1000);
a.reset();
}
std::cout << "will exit" << std::endl;
sleep(5);
std::cout << "exit" << std::endl;
return 0;
}
发现没,程序执行异常crash了。还记得吗,对于继承了enable_shared_from_this的对象,不能在析构函数中使用对象,上面情况是不是命中了这个禁忌?
针对这个问题,weak_ptr能提供一个很好的解决方案,关于weak_ptr后面会专门介绍,使用weak_ptr方案的代码如下
#include <iostream>
#include <memory>
#include <unistd.h>
#include <thread>
#include <atomic>
struct A : public std::enable_shared_from_this<A> {
A() {
inited_.store(true);
}
bool isInit() {
return inited_.load();
}
void debug() {
std::cout << "debug" << std::endl;
}
void init() {
std::weak_ptr<A> weak_ptr = shared_from_this();
thread_ = std::thread([weak_ptr]() {
usleep(1000 * 10000);
auto this_ = weak_ptr.lock();
if (this_) {
this_->debug();
} else {
std::cout << "null of this_" << std::endl;
}
});
}
~A() {
std::cout << "pre ~A" << std::endl;
usleep(1000 * 1000);
inited_.store(false);
std::cout << "wait ~A" << std::endl;
if (thread_.joinable()) {
thread_.join();
}
std::cout << "post ~A" << std::endl;
}
private:
std::atomic<bool> inited_;
std::thread thread_;
};
int main() {
{
auto a = std::make_shared<A>();
a->init();
usleep(100 * 1000);
a.reset();
}
std::cout << "will exit" << std::endl;
sleep(5);
std::cout << "exit" << std::endl;
return 0;
}
关于weak_ptr 以及其方法 lock() ,可以先参考 std::weak_ptr