这里其实感觉没什么好说的主要记录一下读的时候的一些问题
1.1.2mutexlock和mutexlockguard
这部分的封装在2.4有写,还没看,后面补上,emm我平常也就是{lockguard}这样子写一下锁
1.1.3线程安全的Counter
这里是真学到了,boost::noncopyable,继承之后可以把拷贝构造和赋值构造干掉
可以看看源码
emm因为没有看到后面我就用自己的写法写了一下
#include <iostream>
#include <mutex>
#include <memory>
#include <boost/noncopyable.hpp>
class Counter : boost::noncopyable//可以把拷贝构造和赋值构造给干掉
{
public:
Counter() :_value(0)
{
};
~Counter()
{
};
int value()
{
{
std::lock_guard<std::mutex> mylockguard(mtx);
return _value;
}
};
int getAndIncrease()
{
{
std::lock_guard<std::mutex> mylockguard(mtx);
int ret = _value++;
return ret;
}
}
private:
std::mutex mtx;
int _value;
};
这里是线程安全的因为对_value的操作都是在锁的区域内
1.2对象创建的线程安全
简单来说就是在构造的时候不要暴露this,不然this可能还没构造完就被访问到,所以用二段的写法
1.3销毁太难
大概就是说如果说一对象销毁的时候,他持有的mutex会先死掉,没办法保证析构的线程安全,所以没有办法用mutex解决这个问题
后面也说了析构其实不需要去保护,只有所有线程都访问不到了,析构才是安全的,个人感觉就是智能指针可以处理掉这个问题
然后提到了调用的一种死锁情况swap(a,b),swap(b,a),在并发环境下有可能死锁
解决办法就是比较两个对象的mutex的地址,小或者大的先加锁,反正自己规定
1.4-1.8观察者模式线程安全写法已经C++11后引入的shared_ptr/weak_ptr
提到了观察者模式的多线程情况下的线程安全问题(观察者模式就算,某个对象修改时,其他对象都知道他的修改)
1.Observerable不知道Obsever是不是还活着
2.就算在Obsrever析构的时候去让Observerable解绑,又有一个问题是Observerable是不是还活着,在解绑的时候还没来得及析构掉有没有可能就已经被调用了
然后这里就可以引入shared_ptr/weak_ptr,weak_ptr可以判断对象是不是已经被析构
代码如下
class Observer
{
public:
Observer();
~Observer();
virtual void update() = 0;
private:
};
class Observerable
{
public:
Observerable();
~ Observerable();
void notifyObservers()
{
{
std::lock_guard<std::mutex> mylockguard(mtx);
int i = 0;
for (std::weak_ptr<Observer> obs : observers)
{
std::shared_ptr<Observer> newobs(obs.lock());
if (newobs)newobs->update();
else observers.erase(observers.begin() + i);
i++;
}
}
};
void register_(std::weak_ptr<Observer>x) {};
private:
std::mutex mtx;
std::vector<std::weak_ptr<Observer>> observers;
};
提升之后>=2是因为observers里面有一个,obs.lock()提升之后引用计数器+1,就是2了如果还有其他地方在用他那就是>2了
然后就是std::vector<std::weak_ptr
信号和槽的写法看不懂,打算看看b站的视频之后补上
1.9-1.10 share_ptr
share_ptr本身线程安全,但是他管理的东西不是线程安全的,也就是说读写要加锁
技术陷阱意外延长生命,比如说循环引用,又比如上面那个std::vector<std::weak_ptr
在share_ptr出现后的C++RAII技术应该是每个资源分配也就是new应该交给一个回调也就是shard_ptr
1.11对象池-1.12enable_shared_from_this
当涉及多对象时,如果每次都重新创建一个多少有点浪费时间,so建立一个对象池,要的时候创建进去,存着,直到没人用自己再析构掉
这里就涉及到了之前的问题std::vector<std::weak_ptr
但是还是有问题map中的weak_ptr没有被干掉,虽然他的指向的对象已经寄了
这里就引出了shard_ptr的销毁回调
但是这里的this是有问题的,有可能stockfactory先于stock析构掉,那这个this就是不知道什么玩意了
这个时候就需要share_from_this去延长stockfactory的生命
这里我写了个std标准库版本的,和boost版本有点差异
emm然后一开始我觉得应该在removestock的时候也把stock的内存释放掉,回调里则是判断对象池还在不在,再去考虑释放是调用removestock还是直接delete,但是仔细想一下对象实际是被一个share_ptr管理的,我们应该只有在它引用计数器=0时才考虑干掉它,也就是在除回调回调之外的地方不要delete,因为它有可能在多线程情况下移出了map但是还有其他线程在用它,引用计数器>0,如果在移出map的时候去delete那么会出现宕机的情况,所以在C++11share_ptr出现的时代,内存管理不由我们程序员乱来,share_ptr是几乎完美的解决,不需要我们思考new和delete的配对
//对象定义
class Stock
{
public:
Stock(std::string key) {
attribute = key;
}
std::string attribute;
};
//对象池
class StockFactory:public boost::noncopyable,std::enable_shared_from_this<StockFactory>
{
public:
StockFactory();
~StockFactory();
std::shared_ptr<Stock> get(const std::string& key)
{
std::shared_ptr<Stock> pstock;
{
std::lock_guard<std::mutex> mylockguard(mtx);
//注意是引用的
std::weak_ptr<Stock>& wkstock = stocks_[key];//拿到对应的管理Stock的弱引用指针
pstock = wkstock.lock();//升级
//升级失败的时候就是要重新生成一个
if (!pstock)
{
//绑定对应的回调函数,std的和boost不太一样,要指定函数来源哪里就是要加一个this
pstock.reset(new Stock(key), std::bind(&StockFactory::weakdeletecallback,this,
std::weak_ptr<StockFactory>(shared_from_this()), std::placeholders::_1));
wkstock = pstock;
}
return pstock;
}
}
private:
std::mutex mtx;
std::unordered_map<std::string, std::weak_ptr<Stock>> stocks_;
void weakdeletecallback(const std::weak_ptr<StockFactory>& wkFactory, Stock* stock)
{
//升级
std::shared_ptr<StockFactory>factory(wkFactory.lock());
if (factory)
{
//升级成功就是对象池还存在,可以删除掉对应的对象,否则就已经没有在map里面
factory->removestock(stock);
}
else
{
//把对象空间释放
delete stock;
}
}
void removestock(Stock* stock)
{
if (stock)
{
{
//把map中的删掉
std::lock_guard<std::mutex> mylockguard(mtx);
stocks_.erase(stock->attribute);
//delete stock这个是不对的,不要delete它
}
}
}
};