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=sp4
shared_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=sp4
shared_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单例在创建初始化,则不能用饿汉模式,饿汉模式无法保证创建初始化顺序,懒汉模式可以手动控制。