1,多线程概论
2,std::ref()将值转为引用,std::cref()将值转为常引用
第一种:不推荐
int ca = 5;
int& cca = ca;
thread ttt(testC, std::ref(ca));//可以
//thread ttt(testC, cca);//不可以
第二种:推荐
thread ttt([&] {testC(ca);
});
3,多线程原则
4,mutex的使用
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include<cassert>
#include<thread>
#include<map>
#include<fstream>
#include<atomic>
#include<mutex>
using namespace std;
template<typename T>
class AddCount
{
public:
AddCount() {
}
AddCount(int a) {
}
//std::atomic<int> count = 0;
int count = 0;
void Add() {
count++;
}
//std::atomic<int> resource = 0;
int resource = 0;
void AddResource() {
resource++;
if ((resource / count) != 1) { cout << "badthing" <<"resource/ count:"<< resource / count <<"fenbieshi:"<< resource <<" , "<< count << endl; }
}
void LockMutex() {
m_mutex.lock();
}
void UnlockMutex() {
m_mutex.unlock();
}
std::mutex m_mutex;
};
template<typename T>
void HandVec(T& count , vector<int>::iterator begin,vector<int>::iterator end) {
for (; begin != end; ++begin)
{
count.LockMutex();
count.Add();
count.AddResource();
count.UnlockMutex();
}
}
void testC(int& x) {
cout << x << endl;
}
int main()
{
AddCount<int> count1;
vector<int> seqVec;
for (int i = 0; i < 100000; i++)
{
seqVec.push_back(i);
}
auto f = [&]() {
HandVec(count1, seqVec.begin(), seqVec.end());
};
f();
cout << "count1的数量:" << count1.count << endl;
AddCount<int> count2;
auto twoP = seqVec.begin()+seqVec.size() / 3;
auto threeP= seqVec.begin() + (2*seqVec.size()) / 3;
AddCount<int> count3;
thread a([&] {
HandVec(count2, seqVec.begin(), twoP);
});
AddCount<int> count4;
thread b([&] {
HandVec(count2, twoP, threeP);
});
AddCount<int> count5;
HandVec(count2, threeP, seqVec.end());
a.join();
b.join();
cout << "count2的数量:" << count2.count << endl;
cout << "count345的数量:" << count3.count+ count4.count+count5.count << endl;
cin.get();
return 0;
}
注意:这个程序虽然能正确执行,但是如果在临界区里面抛出异常,会在异常处跳过,直接到catch里,不会执行UnlockMutex
1),将上面的类修改一下 mutable std::mutex m_mutex; //mutable这个锁,既可以在正常函数可以用锁,也可以在函数名后加const的函数内使用
2),用lock_guard来管理mutex ,(因为c++类里的构造和析构是肯定会被执行的,这个原理)
3),std::lock(),后面跟2及以上参数,参数可以是对象(类内部必须有mutex类型),可以是mutex。一般和lock_guard合用,用法如下:
std::lock(myMutex, myMutex1);
std::lock_guard<mutex> myLock(myMutex,std::adopt_lock);//多线程下cout会输出乱,这里加上锁,可以避免
cout << i << endl;
结果:
拓展:
不加锁的结果:
不加锁,使结果正确,用c语言的方法:因为c语言的printf方法是线程安全的
结果:
原因:为什么cout不是线程安全的,因为cout虽然<<可以很多写在一行,但是实际相当于分开来执行,那么在多线程中,就相当于分开执行,所以不是线程安全的
3,thread.join (thread.joinable)和 thread.detach
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include<cassert>
#include<thread>
#include<map>
#include<fstream>
#include<atomic>
#include<mutex>
using namespace std;
bool isReady = false;
void Mythread1(int i) {
while (!isReady)
{
}
cout << i << endl;
}
int main()
{
vector<thread> mythreadVecPool;
for (int i = 0; i < 4; i++)
{
mythreadVecPool.emplace_back(Mythread1, i);
}
for (auto& v: mythreadVecPool)
{
if (v.joinable())
v.join();//这里会使主线程等待在这里
}
//for (int i = 0; i < 4; i++)
//{
// if (mythreadVecPool[0].joinable()) mythreadVecPool[0].join();
//}
cout << "finish" << endl;
cin.get();
return 0;
4,thread间的交互
1)std::this_thread::sleep_for(std::chrono::minutes(1));//this_thread当前线程 ///chrono时间
2)实际中99%都需要共享一个数据,问题是多线程要想和单线程用一样的时间,多线程开线程,加锁,解锁,需要时间
3)不正确交互方法:
#include <iostream>
#include <vector>
//#include <string>
#include <algorithm>
#include<cassert>
#include<thread>
#include<map>
#include<fstream>
#include<atomic>
#include<mutex>
#include<chrono>
#include<cstring>
#include<condition_variable>
//using namespace std;
std::string ss("11");
char aaa[] = "11111";
std::atomic< bool> isReady = false;
std::mutex myMutex;
std::mutex myMutex1;
std::unique_lock<std::mutex> unilock;
void Mythread1(int i) {
std::cout<< strlen(aaa)<<std::endl;
while (!isReady)
{
//std::this_thread::yield();//该线程到这里,会将cpu让出来,让给其他线程,
//不能减少cpu占用还是60%左右
//但是在这里没用,一个线程到这里,将cpu让出来了,但是另一个线程又会直接抢占
//另一个线程执行到这里,也会让出cpu,另一个又会直接抢占,所以等于没作用
//std::this_thread::sleep_for(std::chrono::seconds(1));//相当于时间片轮询,可以减少cpu占用 5%左右
//但不是特别好的方法
}
//std::lock_guard<mutex> myLock(myMutex);//多线程下cout会输出乱,这里加上锁,可以避免,可以用printf代替
//cout << i << endl;
printf("%d\n", i);
}
int main()
{
std::vector<std::thread> mythreadVecPool;
for (int i = 0; i < 4; i++)
{
mythreadVecPool.emplace_back(Mythread1, i);
}
std::this_thread::sleep_for(std::chrono::seconds(1));//this_thread当前线程 ///chrono时间
isReady = true;
for (auto& v : mythreadVecPool)
{
if (v.joinable()) {
std::cout << "开始" << std::endl;
v.join();
}
std::cout << "finish1" << std::endl;
}
std::cout << "finish" << std::endl;
std::cin.get();
return 0;
}
4)正确线程间交互方法
condition_variable和unique_lock配合使用
使用方法:
#include <iostream>
#include <vector>
//#include <string>
#include <algorithm>
#include<cassert>
#include<thread>
#include<map>
#include<fstream>
#include<atomic>
#include<mutex>
#include<chrono>
#include<cstring>
#include<condition_variable>
using namespace std;
std::string ss("11");
char aaa[] = "11111";
bool isReady = false;
std::mutex myMutex;
std::mutex myMutex1;
std::condition_variable cv;
std::unique_lock<std::mutex> unilock;
void Mythread1(int i) {
//while (!isReady)//第一种和第二种方法需要在while循环里判断
//{
//std::this_thread::yield();//第一种方法
//该线程到这里,会将cpu让出来,让给其他线程,
//不能减少cpu占用还是60%左右
//但是在这里没用,一个线程到这里,将cpu让出来了,但是另一个线程又会直接抢占
//另一个线程执行到这里,也会让出cpu,另一个又会直接抢占,所以等于没作用
//std::this_thread::sleep_for(std::chrono::seconds(1)); //第二种方法
//相当于时间片轮询,可以减少cpu占用 5%左右
//但不是特别好的方法
//}
std::unique_lock<std::mutex> ulock(myMutex);//第三种方法,不需要while循环
cv.wait(ulock, [] {return isReady; }); //unique_lock配合condition_variable使用
//执行顺序,先unique_lock锁住,cv.wait会解锁ulock,但是在这cv会判断是否return条件是否满足
//如果return条件满足,cv.wait会重新加锁ulock,执行下面的语句
//加锁之后,就保证数据的安全
//这种方法就可以通知另一个线程什么时候开始执行
//std::lock_guard<mutex> myLock(myMutex);//多线程下cout会输出乱,这里加上锁,可以避免,可以用printf代替
//cout << i << endl;
std::printf("%d\n", i);
}
int main()
{
std::vector<std::thread> mythreadVecPool;
for (int i = 0; i < 4; i++)
{
mythreadVecPool.emplace_back(Mythread1, i);
}
std::this_thread::sleep_for(std::chrono::seconds(1));//this_thread当前线程 ///chrono时间//延时的作用
isReady = true;
//cv.notify_one();//通知一个线程
cv.notify_all();//通知所有线程
for (auto& v : mythreadVecPool)
{
if (v.joinable()) {
std::cout << "开始" << std::endl;
v.join();
}
std::cout << "finish1" << std::endl;
}
std::cout << "finish" << std::endl;
std::cin.get();
return 0;
}
结果: