以下程序代码参考来源于
https://www.cnblogs.com/douzujun/p/10810506.html
https://www.cnblogs.com/douzujun/p/10816335.html
本篇只是自己学习的笔记,部分代码添加了自己理解的注释。
一、一个多线程程序示例,与单线程比较
//====================把一个复杂的计算拆分多线程实现的方式,与单线程做比较,可以看到多线程的好处==============
#include<iostream>
#include<thread>
#include<vector>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <future>
using namespace std;
double caculate(int v){
if(v<=0){
return v;
}
//假设这个计算过程很慢
this_thread::sleep_for(chrono::milliseconds(10));
return sqrt((v*v +sqrt((v-5)*(v*2.5))/2.0)/v);
}
template <typename Iter,typename Fun>
double visitRange(thread::id id,Iter iterBegin,Iter iterEnd,Fun func){
auto curId = this_thread::get_id();//获取当前执行线程的ID
if(id==this_thread::get_id()){
cout<<curId<<"hello main thread\n";
}else{
cout<<curId<<"hello work thread\n";
}
double v=0;
for(auto iter = iterBegin;iter!=iterEnd;++iter){
v+=func(*iter);
}
return v;
}
int main(){
auto mainThreadId = this_thread::get_id();
//开启一个单线程
vector<double> v;
for(int i=0;i<1000;i++){
v.push_back(rand());
}
cout<<v.size()<<endl;
double value = 0.0;
auto st = clock(); //需要包含什么头文件?
for(auto & info:v){
value += caculate(info);
}
auto ed = clock();
cout<<"single thread: "<<value<<" "<<ed-st<<"time"<<endl;
//下面使用多线程实现
auto iterMid = v.begin() +(v.size()/2);
//计算后半部分
double anotherV = 0.0;
auto iterEnd =v.end();
st = clock();
thread s([&anotherV,mainThreadId,iterMid,iterEnd](){
anotherV = visitRange(mainThreadId,iterMid,iterEnd,caculate);
});
//计算前半部分
auto halfv = visitRange(mainThreadId,v.begin(),iterMid,caculate);
//关闭线程
s.join();
ed = clock();
cout<<"multi thread: "<<(halfv+anotherV)<<" "<<ed-st <<"time"<<endl;
return 0;
}
执行结果:
1000
single thread: 120540 10623time
1hello main thread
2hello work thread
multi thread: 120540 5343time
二、多线程遇到的问题
共享变量带来的问题:
#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
using namespace std;
class Counter{
public:
void addCount(){
m_count++;
}
int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
int m_count;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addCount(); //计数。
}
}
int main(){
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
//单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
//多线程实现
totalValue =0;
Counter counter2;
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
//线程b负责计算1/3~2/3的工作
thread b([&counter2,&totalValue,iter,iter2](){
realWork(counter2,totalValue,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
thread c([&counter2,&totalValue,iter2,end](){
realWork(counter2,totalValue,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue<<endl;
return 0;
}
执行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 4833168 4.61286e+008
可以发现两次的运算的结果不一致,因为三个线程共享一份资源,出现了重复对同一个数值相加的情况,例如:main、b、c都
对m_count=6加了一次,结果最后结果只是7,这就是共享变量的问题所在
解决方法:
1、不共享变量totalvalue
#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
using namespace std;
class Counter{
public:
void addCount(){
m_count++;
}
int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
int m_count;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addCount(); //计数。
}
}
int main(){
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
//单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
//多线程实现
totalValue =0;
Counter counter2;
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;
return 0;
}
执行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 4307691 9.89328e+008
2、添加原子性
方法2;原子操作变量类型(复杂,适合简单的应用)
/b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
count++: 写入寄存器,寄存器+1,写入内存
average()函数功能是如果Counter2不等于10000000,程序就不退出,如运行截图,由于共享变量counter2, 导致counter2总是无法等于10000000
解决:原子操作变量
只需要把int m_count; 改成 atomic m_count; 即可/
#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
#include <atomic> //添加头文件
using namespace std;
class Counter{
public:
void addCount(){
m_count++;
}
int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
// int m_count;
atomic<int> m_count; //限制为原子操作
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addCount(); //计数。
}
}
bool average(Counter& c,int maxCount){
auto cnt = c.count();
if(cnt == maxCount){
cout<<" ok finished\n";
return true;
}
return false;
}
int main(){
//(1)如果没有必要的话,线程间不要共享资源
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
// 单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
// 多线程实现
totalValue =0;
Counter counter2;
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
// b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;
return 0;
}
执行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 10000000 9.89328e+008
//counter.count一致了
三、多线程操作多个变量
//=新增变量的处理,使用mutex互斥==========
核心部分:
void lockMutex() { m_mutex.lock(); }
void unlockMutex() { m_mutex.unlock(); }
使用:
c.lockMutex();
c.addCount();
c.addResource(1);
c.unlockMutex();
note:注意临界区的使用,否则可能会发生死锁
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
#include <atomic>
#include <mutex>
using namespace std;
class Counter{
public:
void addCount(){
m_count++;
}
int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
void addResource(int a){
m_totalResource++;
}
int aveResource(){
if(m_count==0)
return 1;
return m_totalResource/m_count;
}
void lockMutex(){ m_mutex.lock();}
void unlockMutex(){ m_mutex.unlock();}
private:
// int m_count;
atomic<int> m_count;
atomic<int> m_totalResource;
mutex m_mutex;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.lockMutex();
c.addCount(); //计数。
c.addResource(1);
c.unlockMutex();
}
}
bool average(Counter& c,int maxCount){
auto cnt = c.count();
//需要上锁的操作
c.lockMutex();
auto ave = c.aveResource();
if(ave !=1) cout<<"has bad thing happened\n";
c.unlockMutex();
if(cnt == maxCount){
cout<<" ok finished\n";
return true;
}
return false;
}
int main(){
//(1)如果没有必要的话,线程间不要共享资源
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
// 单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
// 多线程实现
totalValue =0;
Counter counter2;
thread printCount([&counter2](){
while(!average(counter2,10000000)){ }
});
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
// b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
// cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;
auto realTotalCount = counter2.count();
totalValue += totalb +totalc;
cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;
printCount.join();
return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
ok finished
total times use multithread: 10000000 9.89328e+008
四、把锁写到接口里
#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;
class Counter{
public:
Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行
int count() const{ //添加const约束后,需要把mutex设置称为mutable类型
m_mutex.lock();
auto r =m_count;
m_mutex.unlock();
return r;
}
int aveResource(){
m_mutex.lock();
if(m_count==0){
m_mutex.unlock();
return 1;
}
auto r =m_totalResource/m_count;
m_mutex.unlock();
return r;
}
void addResourceAddCount(int r){
m_mutex.lock();
addCount();
addResource(r);
m_mutex.unlock();
}
private:
// int m_count;
// atomic<int> m_count;
// atomic<int> m_totalResource;
void addResource(int a){
m_totalResource++;
}
void addCount(){
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addResourceAddCount(1);
}
}
bool average(Counter& c,int maxCount){
auto cnt = c.count();
auto ave = c.aveResource();
if(ave !=1) cout<<"has bad thing happened\n";
if(cnt == maxCount){
cout<<" ok finished\n";
return true;
}
return false;
}
int main(){
//(1)如果没有必要的话,线程间不要共享资源
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
// 单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
// 多线程实现
totalValue =0;
Counter counter2;
thread printCount([&counter2](){
while(!average(counter2,10000000)){ }
});
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
// b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
// cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;
auto realTotalCount = counter2.count();
totalValue += totalb +totalc;
cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;
printCount.join();
return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
ok finished
total times use multithread: 10000000 9.89328e+008
五、自定义锁
原理:利用类对象使用完成后会自动调用析构函数,把上锁操作放在lock的构造函数中
释放锁操作放在lock的析构函数中,我们只用管定义,通过隐式调用构造和析构函数实现
我们想要的操作。
include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;
template <typename T>
class Lock{
public:
Lock(T& mutex):m_mutex(mutex){
m_mutex.lock();
}
~Lock(){
m_mutex.unlock();
}
private:
T& m_mutex;
};
class Counter{
public:
Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行
int count() const{ //添加const约束后,需要把mutex设置称为mutable类型
Lock<mutex> lock(m_mutex);
return m_count;
}
int aveResource(){
Lock<mutex> lock(m_mutex);
if(m_count==0){
return 1;
}
return m_totalResource/m_count;
}
void addResourceAddCount(int r){
Lock<mutex> lock(m_mutex);
addCount();
addResource(r);
// m_mutex.unlock(); //为什么要这一句?不用的,博主的代码有错
}
private:
// int m_count;
// atomic<int> m_count;
// atomic<int> m_totalResource;
void addResource(int a){
m_totalResource++;
}
void addCount(){
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addResourceAddCount(1);
}
}
bool average(Counter& c,int maxCount){
auto cnt = c.count();
auto ave = c.aveResource();
if(ave !=1) cout<<"has bad thing happened\n";
if(cnt == maxCount){
cout<<" ok finished\n";
return true;
}
return false;
}
int main(){
//(1)如果没有必要的话,线程间不要共享资源
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
// 单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
// 多线程实现
totalValue =0;
Counter counter2;
thread printCount([&counter2](){
while(!average(counter2,10000000)){ }
});
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
// b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
// cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;
auto realTotalCount = counter2.count();
totalValue += totalb +totalc;
cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;
printCount.join();
return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
ok finished
total times use multithread: 10000000 9.89328e+008
六、使用STL的lock_guard
把上面自定义的lock换成lock_guard
#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;
class Counter{
public:
Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行
int count() const{ //添加const约束后,需要把mutex设置称为mutable类型
lock_guard<mutex> lock(m_mutex);
return m_count;
}
int aveResource(){
lock_guard<mutex> lock(m_mutex);
if(m_count==0){
return 1;
}
return m_totalResource/m_count;
}
void addResourceAddCount(int r){
lock_guard<mutex> lock(m_mutex);
addCount();
addResource(r);
}
private:
void addResource(int a){
m_totalResource++;
}
void addCount(){
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
};
int work(int a){
return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
for(;b!=e;++b){
totalValue +=work(*b);
c.addResourceAddCount(1);
}
}
bool average(Counter& c,int maxCount){
auto cnt = c.count();
auto ave = c.aveResource();
if(ave !=1) cout<<"has bad thing happened\n";
if(cnt == maxCount){
cout<<" ok finished\n";
return true;
}
return false;
}
int main(){
//(1)如果没有必要的话,线程间不要共享资源
unsigned n = thread::hardware_concurrency(); //检测硬件并发
cout<<n<<" concurret thread are support.\n";
// 单线程实现
vector<int> vec;
double totalValue =0;
for(int i=0;i<10000000;i++){
vec.push_back(rand()%100); //放入1~100的随机数
}
Counter counter;
realWork(counter,totalValue,vec.begin(),vec.end());
cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;
// 多线程实现
totalValue =0;
Counter counter2;
thread printCount([&counter2](){
while(!average(counter2,10000000)){ }
});
auto iter = vec.begin()+(vec.size()/3);
auto iter2 = vec.begin()+(vec.size()/3*2);
// b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
//线程b负责计算1/3~2/3的工作
double totalb = 0; //每一个线程有自己的计数空间
thread b([&counter2,&totalb,iter,iter2](){
realWork(counter2,totalb,iter,iter2);
});
auto end = vec.end();
//线程c负责计算2/3~end的工作
double totalc = 0;
thread c([&counter2,&totalc,iter2,end](){
realWork(counter2,totalc,iter2,end);
});
//main线程计算前1/3的工作
realWork(counter2,totalValue,vec.begin(),iter);
b.join();
c.join();
auto realTotalCount = counter2.count();
totalValue += totalb +totalc;
cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;
printCount.join();
return 0;
}