一.智能指针
1.shared_ptr:
核心:引用计数机制 ,不要用地址传入方式,而是make_type
慎用智能指针的get方法
可以给shared_ptr指定删除器,方法为
#include<memory>
#include<iostream>
using namespace std;
void delete_func(int * p){
cout<<"user deleter"<<endl;
delete p;
}
int main(int argc, char ** argvs){
{ //这种方法无法创建智能指针
//shared_ptr<int> p = new int(3);
//make_shared 无法指定删除器
shared_ptr<int> p (new int(30),delete_func);
}
cout<<"main release" <<endl;
return 0;
}
注意要点:
1.不要用一个原始指针创建多个智能指针
#include<memory>
#include<iostream>
using namespace std;
void delete_func(int * p){
cout<<"user deleter"<<endl;
delete p;
}
int main(int argc, char ** argvs){
{ //这种方法无法创建智能指针
//shared_ptr<int> p = new int(3);
//make_shared 无法指定删除器
int * raw_ptr = new int(30);
//双重析构错误
shared_ptr<int> p(raw_ptr);
shared_ptr<int> q(raw_ptr);
}
cout<<"main release" <<endl;
return 0;
}
//错误信息
root@DESKTOP-7R6L4Q8:~/code/linux_c++/stl and etc# ./ptr
free(): double free detected in tcache 2
Aborted
2.不要在函数实参中创建shared_ptr
//函数参数的计算顺序由编译器决定,如果从右向左并且g产生异常,那么就造成
//内存泄漏
function(shared_ptr<int>(new int), g()); //有缺陷
3.注意如果一个对象要返回自己的this指针作为shared_ptr的参数,那么要使用enable_shared_from_this,否则双重析构
//正确
class A:public enable_shared_from_this<A>{
public:
shared_ptr<A> my_(){
return shared_from_this();
}
};
//错误
class B{
public:
shared_ptr<B> my_(){
return shared_ptr<B>(this);
}
};
int main(int argc, char ** argvs){
{
auto p = make_shared<B>();
cout<<p.use_count()<<endl;
shared_ptr<B> p1 = p->my_();
cout<<p.use_count()<<endl;
cout<<p1.use_count()<<endl;
}
cout<<"main release" <<endl;
return 0;
}
运行结果
1
1
1
double free or corruption (out)
Aborted
使用A版本的enable_shared_from_this 后正常运行
4.避免循环引用:
#include <memory>
#include <iostream>
using namespace std;
//循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导
//致两个指针都不会被析构,产生内存泄漏。
//解决的办法是把A和B任何一个成员变量改为weak_ptr,具体方法见weak_ptr章节。
// std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。
class A;
class B;
class A
{
public:
std::shared_ptr<B> bptr;
~A()
{
cout << "A is deleted" << endl;
}
};
class B
{
public:
std::shared_ptr<A> aptr;
~B()
{
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
cout << "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构
return 0;
}
2.unique_ptr
独占的指针,nocopyable,只能通过move传递所有权
3.weak_ptr
一种不控制对象生命周期的智能指针
可以将weak_ptr传给shared_ptr,lock()方法
4.智能指针线程安全性
1.安全:引用计数是安全的(几次赋值就传几次)
2.不安全:多个线程共用一个智能指针(即传引用方式)
void fn(std::shared_ptr<int> &a){
cout<<*(a.get())<<endl;
a.reset();
}
void fn2(std::shared_ptr<int> &a){
sleep(5);
cout<<*(a.get())<<endl;
a.reset();
}
int main()
{
shared_ptr<int> p(new int (0));
std::thread t1(fn,std::ref(p));
std::thread t2(fn2,std::ref(p));
t1.join();
t2.join();
cout<<"end"<<endl;
return 0;
}
这样会造成段错误
解决方式:改成传值即可
void fn(std::shared_ptr<int> a){
cout<<*(a.get())<<endl;
a.reset();
}
void fn2(std::shared_ptr<int> a){
sleep(5);
cout<<*(a.get())<<endl;
a.reset();
}
int main()
{
shared_ptr<int> p(new int (0));
std::thread t1(fn,p);
std::thread t2(fn2,p);
t1.join();
t2.join();
cout<<"end"<<endl;
return 0;
}
但是这时候每个线程依然是指向的同一个对象,多线程的并发性需要自己保证
二.右值引用
消除拷贝,提高性能
左值,表达式结束后任然存在的对象,右值是指表达式结束就不存在的对象
应用:具有大量堆内存的类
forward:仅仅将值的属性完美转发 例子:
#include <memory>
#include <iostream>
#include <thread>
#include<unistd.h>
using namespace std;
template<typename T>
void l_or_r(T &&t){
cout<< " R be called"<<endl;
}
template<typename T>
void l_or_r(T & t){
cout<< " L be called"<<endl;
}
template<typename T>
void test(T && t){
l_or_r(t);//must L be called no matter t L or R
l_or_r(std::move(t));//R be called no matter t L or R
//forward 仅仅转发自己的左右值属性
l_or_r(std::forward<T>(t));//depend on t,just pass the L or R
}
int main()
{
int a;
test(a);
test(8);
return 0;
}
int &&a = 8;
//oops error,a is a l value
//int && b = a;
//correct
int && b = std::forward<int>(a);
三.多线程
1.主要函数
join():等待线程结束
get_id():返回线程的id号
joinable():返回线程是否被detach()
detach():允许当前线程独立运行,当进程被kill那么该线程也会被强制杀死
2.thread_local:申明为thread_local 的变量不论是否被申明为static都会在本地线程中持续存在
3.互斥量:
c++11的四种互斥量:a.std::mutex 独占的互斥量,不能递归使用
b.std::timed_mutex:带计时功能的互斥量,不能递归使用
c.std::recursive_mutex:递归互斥量,不带计时功能(允许同一个线程多次获取同一个互斥量),效率低,能够用普通互斥量代替,不建议使用
d.std::recursive_timed_mutex:带计时功能的递归互斥量
lock_guard于unique_lock的使用和区别
两者都可以实现自动加锁和解锁
unique_lock更加灵活,可以实现临时的加锁和解锁,但性能略差于lock_guard
tips:减少锁的粒度可以用大括号来限定unique_lock的范围
条件变量condition_variable
条件变量使用流程:1.拥有条件变量的线程获取互斥锁2.阻塞直到条件满足3.某个线程使用notify_one或者notify_all唤醒线程,condition_variable需要与unique_lock来使用
std::call_once和std::once_flag的使用
#include<thread>
#include<mutex>
#include<iostream>
using namespace std;
std::once_flag fg1;
void run(){
cout<<" i can be called mamy times"<<endl;
std::call_once(fg1,[](){
cout<<"i be called once"<<endl;
});
}
int main(){
for(int i = 0;i < 4;++i){
thread t(run);
t.join();
}
return 0;
}
4.异步操作
std::async + std::future
#include<thread>
#include<mutex>
#include<iostream>
#include<future>
using namespace std;
int get_a_value(){
this_thread::sleep_for(5s);
return 30;
}
int main(){
//创建一个未来可以取值的整数
future<int> result = std::async(get_a_value);
cout<<"one called"<<endl;
//get方法阻塞等待
cout<<result.get()<<endl;//be 30
cout<<"tow called"<<endl;
return 0;
}
std::package_task,将func与future打包起来
四.std::bind
绑定函数与参数,同时也可以运用std::placeholders来绑定成员函数
#include<iostream>
#include<mutex>
#include<functional>
using namespace std;
class fun_c{
public:
void mem_fn(){
cout<<"this is mem_fn"<<endl;
}
};
int main(){
auto a = new fun_c();
//绑定一个成员变量,std::placeholders给mem_func占一个位
auto func_bind = std::bind(&fun_c::mem_fn,std::placeholders::_1);
func_bind(a);
return 0;
}
五.线程池
thread_pool.h
void getNow(timeval *tv);
int64_t getNowMs();
#define TNOW getNow()
#define TNOWMS getNowMs()
class CZY_ThreadPool{
protected:
struct TaskFunc{
TaskFunc(uint64_t expireTime)
:expire_time_(expireTime)
{}
std::function<void()> func_;
int64_t expire_time_ = 0;
};
typedef shared_ptr<TaskFunc> TaskFuncPtr;
public:
CZY_ThreadPool();
//虚析构函数,基类的析构函数必须是虚函数
virtual ~CZY_ThreadPool();
bool init(size_t num);
size_t getThreadNum(){
std::unique_lock<std::mutex> lock(mutex_);
return therads_.size();
}
size_t getJobNum(){
std::unique_lock<std::mutex> lock(mutex_);
return tasks_.size();
}
void stop();
bool start();
//可变模板参数
template<class Func,class ...Args>
auto exec(Func && f,Args &&... args) ->std::future<decltype(f(args...))>
{
return exec(0,f,args...);
}
template<class Func,class ...Args>
//exec重载,支持计时操作
auto exec(int64_t timeoutMs,Func && f,Args&& ... args) ->std::future<decltype(f(args...))>
{
int64_t expireTime = (timeoutMs == 0) ? 0 : TNOW + timeoutMs;
//自动推导返回值类型
using retType = decltype(f(args...));
//将函数的参数完美转发即可
//这样可以做到所有的都是无参数的统一形式
auto task = std::make_shared<std::packaged_task<retType()>>(std::bind(std::forward<F>(f),std::forward<Args>(args)...));
TaskFuncPtr fPtr = std::make_shared<TaskFunc> (expireTime);
fPtr->func_ = [](){
(*task)();
};
//互斥访问队列
std::unique_lock<std::mutex> lock(mutex_);
tasks_.push(fPtr);
condition_.notify_one();
return task->get_future();
}
bool waitForAllDone(int millsecond = -1);
protected:
bool get(TaskFuncPtr &task);
bool isTerminate(){
return bterminate_;
}
void run();
protected:
std::mutex mutex_;
vector<std::thread *> therads_;
std::condition_variable condition_;
size_t thread_num;
bool bterminate_;
std::queue<TaskFuncPtr> tasks_;
std::atomic<int> atomic_ {0};
};
int gettimeofday(struct timeval &tv)
{
#if WIN32
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm. tm_isdst = -1;
clock = mktime(&tm);
tv.tv_sec = clock;
tv.tv_usec = wtm.wMilliseconds * 1000;
return 0;
#else
return ::gettimeofday(&tv, 0);
#endif
}
void getNow(timeval *tv)
{
#if TARGET_PLATFORM_IOS || TARGET_PLATFORM_LINUX
int idx = _buf_idx;
*tv = _t[idx];
if(fabs(_cpu_cycle - 0) < 0.0001 && _use_tsc)
{
addTimeOffset(*tv, idx);
}
else
{
TC_Common::gettimeofday(*tv);
}
#else
gettimeofday(*tv);
#endif
}
int64_t getNowMs()
{
struct timeval tv;
getNow(&tv);
return tv.tv_sec * (int64_t)1000 + tv.tv_usec / 1000;
}
thread_pool.cpp
#include "thread_pool.h"
CZY_ThreadPool::CZY_ThreadPool()
:thread_num(1),bterminate_(false)
{}
CZY_ThreadPool::~CZY_ThreadPool(){
stop();
}
bool CZY_ThreadPool::init(size_t num){
std::unique_lock<mutex> lock(mutex_);
if(!therads_.empty()){
return false;
}
thread_num = num;
return true;
}
void CZY_ThreadPool::stop(){
{
std::unique_lock<mutex> lock(mutex_);
bterminate_ = true;
condition_.notify_all();
}
for(size_t i = 0;i < therads_.size() ; ++i){
//将所有未悬挂的任务运行
if(therads_[i]->joinable()){
therads_[i]->join();
}
delete therads_[i];
therads_[i] = nullptr;
}
std::unique_lock<mutex> lock(mutex_);
//保留capacity,重置size,调用析构
//这里的threads都被设置成了nullptr
therads_.clear();
}
bool CZY_ThreadPool::start(){
std::unique_lock<mutex> lock(mutex_);
if(!therads_.empty()){
return false;
}
for(int i = 0; i < thread_num ; ++i){
//这里都是推入的run函数,run函数阻塞读任务并且运行
therads_.push_back(new thread(&CZY_ThreadPool:: run,this));
}
return true;
}
bool CZY_ThreadPool::get(TaskFuncPtr& task){
std::unique_lock<std::mutex> lock(mutex_);
//条件变量自动加锁,释放锁
if(tasks_.empty()){
//把condition的条件写在wait里,这样可以避免
condition_.wait(lock,[this] {return bterminate_ || !tasks_.empty();});
}
if(bterminate_){
return false;
}
if(!tasks_.empty()){
task = std::move(tasks_.front());
tasks_.pop();
return true;
}
return false;
}
void CZY_ThreadPool::run(){
while(!isTerminate()){
TaskFuncPtr task;
bool ok = get(task);
if(ok){
++atomic_;
try{
if(task->expire_time_ != 0 && task->expire_time_ < TNOWMS){
//超时任务,打印日志
}
else{
//func是用用户传入的执行函数
task->func_();
}
}
catch(...){
}
--atomic_;
std::unique_lock<std::mutex> lock(mutex_);
if(atomic_ == 0 && tasks_.empty()){
condition_.notify_all();
}
}
}
}
bool CZY_ThreadPool::waitForAllDone(int millssecond){
std::unique_lock<std::mutex> lock(mutex_);
if(tasks_.empty()){
return true;
}
if(millssecond < 0){
condition_.wait(lock,[this] {return tasks_.empty();});
return true;
}
else{
return condition_.wait_for(lock, std::chrono::milliseconds(millssecond), [this] { return tasks_.empty(); });
}
}