class TA
{
public:
void operator () ()
{
cout << "thread start" << endl;
cout << "thread end" << endl;
}
};
int main()
{
TA ta;
thread mythread(ta);
mythread.join();
cout << "Hello World!" << endl;
return 0;
}
void myprint(const TA &pmybuf)
{
cout << " my print" << endl;
}
int main()
{
int mvar = 1;
std::thread mytobj(myprint, A(mvar));
...
}
标题:传递临时对象作为线程参数
1.
在创建线程的同时构造临时变量的方法是可行的
a) 若传递int这种简单类型,建议都是值传递,不要用引用,防止节外生枝。
b) 如果传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后再函数参数里,
用引用来接,否则系统还多构造一次对象,浪费。
终极结论:
c)建议不适用detach(),只使用join(),这样就不存在局部变量失效导致线程对内部的非法引用问题
- 线程id可以用c++标准库里的函数来获取。std::this_thread::get_id();
- 传递类对象,智能指针作为线程参数
3.1 std::ref()函数
A myobj(10);
std::thread mytobj(myprint, std::ref(myobj));
3.2
void myprint(unique_ptr(int) pzn)
{
}
int main()
{
unique_ptr<int> myp(new int(100));
std::thread mytobj(myprint, std:move(myp);
}
- 用成员函数指针作为线程参数
class A
{
void thread_work(int num)
{
}
}
int main()
{
A myobj(10);
std::thread mytobj(&A::thread_work, &myobj, 15);//第二个参数是引用,才能保证线程里用的是同一个对象
mytobj.join();
}
class A
{
void operator()(int num)
{
}
}
int main()
{
A myobj(10);
std::thread mytobj(std::ref(myobj), 15);
mytobj.join();
}
多线程操作
int main()
{
A myobj;
std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);
std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
myOutMsgObj.join();
myInMsgObj.join();
}
5.1
std::lock_guard<std::mutex> sguard(my_mutex);//原理是:构造函数里执行了lock(),退出的时候析构调用了unlock();
5.2
死锁的一般解决方案:只要两个互斥量上锁顺序一致就不会死锁。
std::lock(my_mutex1, my_mutex2);//有一个锁不住就会都释放
5.3
std::lock(my_mutex1, my_mutex2);//一次锁定多个互斥量
std::lock_guard<std::mutex> sguard(my_mutex1,std::adopt_lock);//这样这一行就不会lock
unique_lock 灵活性比lock_guard好一些,但效率上低一些,内存占用上多一些
std::unique_lock<std::mutex> sguard(my_mutex);
unique_lock的第二个参数
std::adopt_lock:表示这个互斥量已经被lock(必须要把互斥量提前lock,否则会报异常)
标记的效果就是,假设调用方已经拥有了互斥量的所有权(已经被lock()成功了)
用adopt_lock的前提是自己先把mutex先lock上
std::try_to_lock :尝试用mutex()的lock去锁定这个mutex(),但如果没有锁定成功,也会立即返回,并不会阻塞在那里
前提是你自己不能先去lock()
if(sbguard1.own_lock())
{
//拿到了锁
}
std::defer_lock: 并没有给mutex加锁;初始化了一个没有加锁的mutex。
用defer_lock的前提是你不能自己先lock,否则会报异常。
- unique_lock的成员函数
lock(),unlock(),try_lock(),release()
7.1
std::unique_lock<std::mutex> sguard(my_mutex1,std::defer_lock);
sguard.lock();//示例
//处理共享代码。。。。
sguard.unlock();//
//处理非共享代码。。。。
sguard.lock();
//处理共享代码。。。。
//不需要再unlock().
7.2 try_lock();//尝试给互斥量加锁,如果拿不到锁返回false,如果能拿到锁,返回true。这个函数不阻塞
if(sguard.try_lock() == true)
{
}
7.3 release():返回它所管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
std::unique_lock<std::mutex> sguard(my_mutex1)
std::mutex *ptx = sguard.release();//现在你有责任自己解锁这个my_mutex1;
......
ptx->unlock();//自己unlock()
总结:lock锁住的大妈段越少,执行越快,整个程序运行效率越高。
有人把锁头锁住的diamagnetic多少成为所得粒度,粒度一般用粗细来描述;代码少,粒度细,diamagnetic多。粒度粗。
尽量选择合适粒度的代码进行保护。
7.4 unique_lcok所有权的传递
std::unique_lockstd::mutex sguard(my_mutex1)
sguard拥有my_mutex1的所有权,所有权可以转移,但不能复制。
std::unique_lock<std::mutex> sguard1(my_mutex1)
std::unique_lock<std::mutex> sguard2(std::move(sguard1))//转移所有权,sguard1为空,sguard2与my_mutex1绑定
- 设计模式
8.1 单例设计模式
单例:整个项目中,有某个或者某些特殊的类,属于该类的对象,我只能创建一个,多了创建不了
class MyCAS
{
private:
MyCAS();
private:
static MyCAS *m_instance;
public:
static MyCAS *GetInstance()
{
if(m_instance = NULL)
{
m_instance = new MyCAS();
static CGarhuishou cl;
}
return m_instance;
}
class CGarhuishou
{
~CGarhuishou()
{
if(MyCAS::m_instance)
{
delete MyCAS::m_instance;
MyCAS::m_instance = NULL;
}
}
}
void func()
{
cout << ""<< endl;
}
}
int main()
{
MyCAS *p_a = MyCAS::GetInstance();
p_a->func();
MyCAS::GetInstance()->func();
}
8.2 单例设计模式共享数据问题分析、解决
static MyCAS *GetInstance()
{
if(m_instance = NULL)
{
m_instance = new MyCAS();
static CGarhuishou cl;
}
return m_instance;
}
8.3 call_once()
std::mutex resource_mutex;
std::once_flag g_flag;
class MyCAS
{
static void CreatInstance()
{
m_instance = new MyCAS();
static CGarhuishou cl;
}
}
std::call_once(g_flag, CreatInstance);//两个线程同时执行到这里,其中一个线程要等另一个线程执行完毕CreatInstance ,第二个线程才决定要不要执行CreatInstance;
std::condition_variable my_cond;
void inMsgRecvQueue()
{
std::unique_lock<std::mutex> sguard1(my_mutex1);
my_cond.notify_one();
}
void outMsgRecvQueue()
{
std::unique_lock<std::mutex> sguard(my_mutex1);
//如果第二个参数lambda表达式返回值是false,那么wait()将解锁互斥量,并阻塞到本行。堵塞到什么时候呢,阻塞到其他某个线程调用notify_one()成员函数为止.
//如果wait没有第二个参数,my_cond.wait(sguard),那个跟第二个参数返回false的结果一样
//a) wait()恢复后,不断的尝试获取互斥量锁,如果获取不到,那么流程就卡在wait这等着获取,,如果获取到wait()就继续执行
//b)
// b.1) 如果wait有第二个参数lambda(),就判断整个lambda表达式,如果返回值是false,那么wait()将解锁互斥量,并阻塞到本行,等待被notify_one()唤醒
//b.2)如果返回值是true,那么wait()返回,流程继续(此时互斥锁锁着)
// b.3)如果wait没有第二个参数,那么wait()返回,流程继续
my_cond.wait(sguard, [this]{
if(!msgRecvQueue.empty())
return true;
return false;
})
}
9.1 notify_all()
通知所有线程
std::async
//启动一个异步任务:局势创建一个线程并开始执行对应的线程入口函数,它返回一个std::guture对象。
//std::future对象里边就含有线程入口函数所返回的结果,通过get()获取。
int mythread()
{
return 5;
}
int main()
{
std::future<int> result = std::async<mythread>;//创建一个线程并开始执行,绑定关系,流程并不会卡到这里
cout << result.get() << endl;//卡在这里等待mythread执行完毕,拿到结果。只能调用一次,不能调用多次。
//result.wait();//等待线程返回,本身并不返回结果
return 0;
}
10.1
std::packaged_task 打包任务,把任务包装起来
//是个类模板,他的模板参数是各种可调用对象,通过,std::pakcaged_tak来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
std::packaged_task<int(int)> mypt(mythread);//我们把函数mythread通过packaged_task包装起来
std::thread tl(std::ref(mypt), 1);//线程直接开始执行,第二个参数作为线程入口函数的参数
tl.join();//等待线程执行完毕
std::future<int> result = mypt.get_future();
vector <std::packaged_tak<int(int)> > mytasks;
10.2
std::promise,类模板
//我们能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来用
//通过promis保存一个值,在将来某个时刻我们通过把一个future绑定到整个promise上来得到这个绑定的值
void mythread(std::promise<int> &tmpp)
{
}
int main()
{
std::promise<int> myprom;//
std::thread tl(mythread, std::ref(myprom), 100);
tl.join();
std::future<int> ful = myprom.get_future();
auto result = ful>get();
}
10.3
//如果能够用最少的东西能够写出一个文丁、高效的多线程程序,更值得赞善。
//为能够读懂高手的代码铺路
std::future<int> result = std::async<mythread>;
std::future_status status = result.wait_for(std::chrono::second(6));// 等待6s
if ( status = std::future_status::deferre) // 延迟 std::future_status::timeout //超时状态 std::future_status::ready //ready状态
11.1
std::shared_future 也是个类模板,get(0函数复制数据
std::shared_future<int> result_s(std::move(result));
- 原子操作st::atomic
12.1 原子操作概念引出范围
互斥量::多线程编程中保护共享数据
原子操作:理解成一种不需要用到互斥量加锁(无锁)技术的多线程并发编程方式
//或者是在多线程中不会被打断的 程序执行片段; 原子操作效率上比互斥量更胜一筹
//互斥量的加锁一般是针对一个代码段,而原子操作针对的一般是一个变量,而不是一个代码段
//原子操作一般都是指”不可分割的操作“,要么完成,要么不完成。
//一般用于计数或者统计(累计或接收的数据包)
std::atomic<int> g_mycount = 0;
12.2
std::async 深入谈
参数: std::launch::defered[延迟调用],std::launch::async[强制创建一个线程]
std::async()我们一般不叫创建线程,我们一般叫它创建一个异步任务
std::async和std::thread最明显的不同就是async有时候并不创建新线程
12.3
std::async 和std::thread的区别
a)std::thread :创建线程,如果系统资源紧张,创建线程失败,那么整个程序就会报异常崩溃。
b)std::async 一般不会报异常崩溃,因为系统资源紧张导致无法创建线程的时候,不加额外参数的async的调用就不会创 //建新线程,而是后续谁调用了result.get()获取结果,那么异步让你无就运行在这条get()语句所在的线程上
-
windows临界区
自动析构技术
std::lock_guardstd::mutex sbguard(my_mutex);
-
recursive_mutex 递归的互斥量
recursive_mutex递归的独占互斥量:允许同一个线程,同一个互斥量多次被lock,效率上比mutex低。 -
带超时的互斥量std::timed_mutex 和 std::recurisive_timed_mutex
std::timed_mutex 带超时功能的独占互斥量
std::recurisive_timed_mutex带超时功能的递归独占互斥量
//try_lock_for();//等待一段时间,超时未拿到锁,继续执行
//try_lock_until ;//
17.线程池
- 线程创建数量
a) 2000个线程基本就是极限
b) 线程数量尽量不要超过500个,能控制在200个以内