c++11新特性-下
c++11的线程库可以跨平台使用。
原子性操作库(atomic)
不需要对原子类型变量进行加锁解锁操作,线程能够对原子类型变量互斥的访问。
atmoic<T> t; // 声明一个类型为T的原子类型变量t
在C++11中,原子类 型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及operator=
#include <atomic>
int main()
{atomic<int> a1(0);//atomic<int> a2(a1); // 编译失败atomic<int> a2(0);//a2 = a1; // 编译失败return 0;
}
thread的使用
仿函数配合thread使用
//仿函数配合thread使用
atomic<int> x = 0;
struct Add
{void operator()(int n){for (int i = 0; i < n; ++i)++x;}
};
thread t1(Add(), 1000000);
也可以使用匿名对象。
lambda配合thread使用
atomic<int> x = 0;
auto add = [&x](int n) {for (int i = 0; i < n; ++i)++x;};
thread t1(add, 1000000);
thread t2(add, 1000000);
cout << t1.get_id() << endl;
cout << t2.get_id() << endl;
t1.join();
t2.join();
cout << x << endl;
优化:
atomic<int> x = 0;
int m, n;
cin >> m >> n;
vector<thread> vthreads;
for (int i = 0; i < m; ++i)
{vthreads.push_back(thread([&x](int count) {for (int i = 0; i < count; ++i)++x;}, n));
}
for (auto& t : vthreads)
{cout << t.get_id() << ".join()" << endl;t.join();
}
cout << x << endl;
vector<thread> vthreads(m);
for (int i = 0; i < m; ++i)
{vthreads[i]=thread([&x](int count) {for (int i = 0; i < count; ++i)++x;}, n);
}
让两个线程分别打印奇偶数
int n = 10;
mutex mtx1, mtx2;
condition_variable cv1, cv2;
thread t1([&]() {for (int i = 0; i < n; i += 2){cout << this_thread::get_id() << ":" << i << endl;cv2.notify_one();cv1.wait((unique_lock<mutex>&)unique_lock<mutex>(mtx1));}});
thread t2([&]() {for (int i = 1; i < n; i += 2){cv2.wait((unique_lock<mutex>&)unique_lock<mutex>(mtx2));cout << this_thread::get_id() << ":" << i << endl;cv1.notify_one();}});
t1.join();
t2.join();
throw可以抛出任意类型的对象。
catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";else return ((double)a / (double)b);
}void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}
int main()
{try {Func();}catch (const char* errmsg) {cout << errmsg << endl;}catch(...){cout<<"unkown exception"<<endl; }return 0;
}
智能指针
template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){if (_ptr){cout << "delete: " << _ptr << endl;delete _ptr;}}
private:T* _ptr;
};
RAII是一种托管资源的方式,智能指针是依靠这种RAII实现的。
C++98 auto_ptr
将管理权转移
缺陷:ap2=ap1时,ap1将悬空,访问就会报错。
auto_ptr<int> ap1(new int);
auto_ptr<int> ap2 = ap1;
C++11 unique_ptr
缺陷:无法应对需要拷贝的场景。
unique_ptr<int> ap1(new int);
C++11 shared_ptr
namespace sp
{template<class T>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}shared_ptr(shared_ptr<T>& sp):_ptr(sp._str), _pcount(new int(1)){++(*_pcount);}~shared_ptr(){if (--(*_pcount)==0&&_ptr){cout << "delete: " << _ptr << endl;delete _ptr;_ptr = nullptr;}}private:T* _ptr;int* _pcount;};
}
//sp1=sp4shared_ptr<T>& operator=(shared_ptr<T>& sp){if (this != &sp){if (--(*_pcount) == 0){delete _pcount;delete _ptr;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}
引用计数为0时才能对_ptr进行释放,否则会发生内存泄漏。
++操作最终会被翻译为3条汇编指令。
namespace sp
{template<class T>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)),_pmtx(new mutex){}shared_ptr(shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount),_pmtx(sp._pmtx){//++(*_pcount);add_ref_count();}void add_ref_count(){_pmtx.lock();++(*_pcount);_pmtx.unlock();}void release(){bool flag = true;_pmtx.lock();if (--(*_pcount) == 0 && _ptr){cout << "delete: " << _ptr << endl;delete _ptr;_ptr = nullptr;delete _pcount;_pcount = nullptr;flag = true;}_pmtx.unlock();if (flag == true){delete _pmtx;_ptrx = nullptr;}}//sp1=sp4shared_ptr<T>& operator=(shared_ptr<T>& sp){if (this != &sp){release();_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}~shared_ptr(){release();}private:T* _ptr;int* _pcount;mutex _pmtx;};
}
shared_ptr:不能解决循环引用的问题
weak_ptr: 可以辅助shared_ptr解决循环引用的问题,严格来说weak_ptr不是智能指针。
template<class T>
class weak_ptr
{
public:weak_ptr() = default;weak_ptr(const sp::shared_ptr<T>& sp):_ptr(sp.get_ptr()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get_ptr();return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get_ptr() const{return _ptr;}
private:T* _ptr;
};
struct ListNode
{int val;sp::shared_ptr<ListNode> _spnext;sp::shared_ptr<ListNode> _spprev;ListNode():val(0), _spnext(nullptr), _spprev(nullptr){}~ListNode(){cout << "~ListNode()" << endl;}
};
sp::shared_ptr<ListNode> spn1(new ListNode);
sp::shared_ptr<ListNode> spn2(new ListNode);
spn1->_spnext = spn2;
spn2->_spprev = spn1;
智能指针是RAII设计思想的体现
本质上RAII就是借助了构造函数和析构函数,构造函数和析构函数的特点都是自动调用。
使用RAII思想设计的锁管理守卫
template<class lock>
class LockGuard
{
public:LockGuard(Lock& lock):_lk(lock){_lk.lock();}~LockGuard(){cout << "解锁" << endl;_lk.unlock();}
private:Lock& _lk;
};
如何实现,创建出的类只能在堆上?
class HeapOnly
{
public:static HeapOnly* GetObj(){return new HeapOnly;}HeapOnly(const HeapOnly&) = delete;
private:HeapOnly(){}};
shared_ptr<HeapOnly> sp1(HeapOnly::GetObj());
shared_ptr<HeapOnly> sp2(HeapOnly::GetObj());
如何实现,创建出的类只能在栈上?
class StackOnly
{
public:static StackOnly GetObj(){return StackOnly();//传值返回需要调用拷贝构造}
private:StackOnly() {}};
StackOnly so = StackOnly::GetObj();
StackOnly* p = new StackOnly;//operator new+ 构造函数
构造函数私有化后,这个类就不能被继承了。
单例模式
简单的单例模式
懒汉模式:
class Singleton
{
public:static Singleton* GetInstance(){//sleep(1000);//_mtx.lock();if(_pinst==nullptr){unique_lock<mutex> lock(_mtx);if (_pinst == nullptr){_pinst = new Singleton;}}//_mtx.unlock();return _pinst;}
private:Singleton() {}Singleton(const Singleton& s) = delete;static Singleton* _pinst;static mutex _mtx;
};
Singleton* Singleton::_pinst = nullptr;
mutex Singleton::_mtx;
第一个if是为了防止对象已经创建好之后2,还需要每次加锁,造成锁的浪费
第二个if是防止非原子操作导致多个线程一起写
饿汉模式
class Singleton
{
public:static Singleton* GetInstance(){return &_inst;}Singleton(const Singleton&) = delete;
private:Singleton(){}static Singleton _inst;
};
总结:
1.懒汉模式需要考虑线程安全和释放的问题,实现复杂而饿汉模式实现简单
2.懒汉模式是懒加载模式需要再初始化创建对象,不会影响程序启动,
饿汉模式程序启动时就创建实例化对象,会导致程序变慢
3.如何有多个单例类,假设有依赖关系(B依赖A)要求A单例先创建初始化,B单例在创建初始化,则不能用饿汉模式,饿汉模式无法保证创建初始化顺序,懒汉模式可以手动控制。