1,多线程概论

c++笔记3

 c++笔记3

c++笔记3

 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,多线程原则

c++笔记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++笔记3

拓展:

不加锁的结果:

c++笔记3

不加锁,使结果正确,用c语言的方法:因为c语言的printf方法是线程安全的

c++笔记3

结果:

c++笔记3

原因:为什么cout不是线程安全的,因为cout虽然<<可以很多写在一行,但是实际相当于分开来执行,那么在多线程中,就相当于分开执行,所以不是线程安全的

c++笔记3

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)正确线程间交互方法

c++笔记3

c++笔记3

c++笔记3

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;
}
 

结果:

c++笔记3

相关文章:

  • 2021-08-11
  • 2022-01-14
  • 2021-11-09
  • 2021-09-18
  • 2022-12-23
  • 2021-08-09
猜你喜欢
  • 2021-06-10
  • 2021-07-29
  • 2022-01-20
  • 2021-06-24
  • 2021-06-24
  • 2021-09-13
相关资源
相似解决方案