c++ —多线程学习(持续更新中…)
目录
0.声明
我只是知识的搬运工,谢谢!
1.线程的三种启动方式
(当然前提是要引用头文件)
(1)普通函数启动
#include <thread>
void function(){
//...一些操作;
}
int main(){
//这里的function是自己定义的一个函数;这里的参数是function的传参。
thread myobj(function, 参数);
myobj.join();
}
(2)类中成员函数做线程启动函数
class A{
public:
void myprint(){ //成员函数
//...一些操作
}
}
int main(){
A a;
std::thread myobj(&A::myprint, &a , 参数);
myobj.join();
}
(3)lammda表达式
auto lamthread = []{ cout << "..." << endl;....}
thread a(lamthread);
a.join();
2.使用detach()时注意事项
.*detach()与.join()区别在于后者是主线程等待子线程结束之后在结束,而前者.detach()*则不同,此时主线程不会等待子线程结束。
在传递类对象时,避免隐式转换,要在main函数中提前显示转换,然后在函数参数里,用引用来接,否则系统还会多构造一次对象。
若传递int 这种简单型参数,建议都是值传递,不要用引用。
3.创建和等待多个线程
void myprint(int i){//...}//线程函数公共入口
int main(){
vector <thread> mythreads;//线程型容器
for(int i = 0; i <= 10; ++i){
mythreads.push_back(thread(myprint , i));
}
for(auto iter = mythread.begin(); iter < mythread.end(); ++iter){
iter->join();//主线程等待所有子线程结束之后运行结束
}
cout << "主线程完毕" << endl;
return 0;
}
4.互斥量与死锁问题及其解决方法
(1)互斥量
当同时有多个线程共享数据时,必须考虑互斥量问题。
互斥量即“锁头”。
当一个线程访问并操作共享数据时必须先保护线程中操作共享数据的代码(即上锁),其他线程等待此线程操作完后(即开锁后),才能对共享数据做相应的操作。
这里通过引用头文件#include 中的lock()和unlock()方法进行所谓的开解锁操作。
(注意:lock(),unlock()必须成对出现)
举例代码:
class A{
private:
list <int> date;
mutex my_mutex;
public:
void in(int i){
my_mutex.lock();
date.push_back(i);
my_mutex.unlock();
}
void out (int command){
my_mutex.lock();
if(!date.empty())
{
command = date.front();
date.pop_front();//移除首个数据
my_mutex.unlock();
return true;
}
my_mutex.unlock();
return false;
}
}
(2)死锁问题及其解决方法
死锁问题造成的原因:
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。
死锁问题必须是两个及两个以上互斥量(锁),两个及两个以上的线程。
举例说明:
现有两把锁:my_mutex1,my_mutex2
第一种方案:两把锁上锁顺序一致,即第一条线程中my_mutex1.lock();和my_mutex2.lock();是什们前后位置,在别的线程中就应该是什么顺序。代码简写:
#include<mutex>
{
mutex my_mutex1;//声明互斥量1(即锁)
mutex my_mutex2;//声明互斥量2(即锁)
一个线程中:
my_mutex1.lock();
//一些操作
my_mutex2.lock();
//要保护的代码操作块
my_mutex1.unlock();
my_mutex2.unlock();
另一线程中:
my_mutex1.lock();
//一些操作
my_mutex2.lock();
//要保护的代码操作块
my_mutex1.unlock();
my_mutex2.unlock();
}
第二种方案:运用lock_guard类模板。
lock_guard类模板原理在于,它将lock()写入构造函数,将unlock()写入析构函数中。
std::lock_guardstd::mutex a(my_mutex);
代码简写:
#include<mutex>
{
mutex my_mutex1;//声明互斥量1(即锁)
mutex my_mutex2;//声明互斥量2(即锁)
一个线程中:
std::lock_guard<std::mutex> a1(my_mutex1);
std::lock_guard<std::mutex> a2(my_mutex2);
//要保护的代码操作块
另一线程中:
std::lock_guard<std::mutex> a1(my_mutex1);
std::lock_guard<std::mutex> a2(my_mutex2);
//要保护的代码操作块
}
第三种方案:运用std::lock()函数模板,和std::lockguard类模板
(std::lock()函数模板把不经常用,一般两个互斥量之间应该有一些操作,建议一个一个锁)
#include<mutex>
{
mutex my_mutex1;//声明互斥量1(即锁)
mutex my_mutex2;//声明互斥量2(即锁)
一个线程中:
std::lock(my_mutex1,my_mutex2);
std::lock_guard<std::mutex> a1(my_mutex1,std::adopt_lock);
//std::adopt_lock是一个结构体对象,起标记作用。
//作用就是这个互斥量已经lock()过了就不需要在std::lock_guard<std::mutex>里面对对//象进行再次的lock()了。
std::lock_guard<std::mutex> a2(my_mutex2,std::adopt_lock);
//要保护的代码操作块
另一线程中:
std::lock(my_mutex1,my_mutex2);
std::lock_guard<std::mutex> a1(my_mutex1,std::adopt_lock);
std::lock_guard<std::mutex> a2(my_mutex2,std::adopt_lock);
//要保护的代码操作块
}
自学过程中杂碎cpp知识
(1)list:对频繁地按顺序插入和删除数据是效率高;vector:对随机的插入和删除数据的效率高
(2)使用std::ref()时,说明我们告诉控制台必须要用原来没有拷贝的初始值来做传递,并且此时函数中的参数前不在加const。(声明线程时,括号中&myobj == std::ref(myobj),即第二个参数是引用,才能保证线程里用的是同一对象)
(3)传递智能指针时,用std::move()(把智能指针移到参数中,原先的智能指针指向为空,所以,main中只能用join不能用detach)
Finally Self-introduction
欢迎光临我的博客了解更多