C++多线程编程(一)(使用C++11后的thread类)

it2024-01-20  65

以下程序代码参考来源于 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; }
最新回复(0)