• 【C++】多线程的学习笔记(2)——白话文版(bushi


    目录

    前一篇

    本章内容提要

    使用mutex锁的原因

    mutex锁的概念

    mutex的使用教程

    锁的声明以及命名

    mutex的加锁以及解锁

    例子

    结果

    注意

    mutex的其他方式的锁介绍

    lock_guard

    介绍

    例子

    运行结果

    adopt_lock参数

    unique_lock

    介绍

    try_to_lock

    defer_lock

    release

    例子

    结果

    总结


    前一篇

    第一篇在这

    【C++】多线程的学习笔记——白话文版(bushi-CSDN博客C++ 作为一种强大的编程语言,为多线程编程提供了丰富而灵活的支持。C++ 的标准库提供了头文件,其中包含了用于创建、启动和管理线程的类和函数。通过使用这些多线程库和功能,开发人员可以轻松地引入并发性到自己的应用程序中,实现多线程的并行处理。thread函数中定义线程的语法规如下std::thread 变量名 (函数,传递的参数1,传递的参数2,传递的参数3...)【如果前面加了using namespace std;可以删除std::】https://blog.csdn.net/mumuemhaha/article/details/133468825?spm=1001.2014.3001.5502

    本章内容提要

    上一章我们讲解了如何利用thread库初步进行多线程操作

    这一章,我们主要讲的是锁(其实就是mutex锁)的概念

    使用mutex锁的原因

    在上一章的多线程操作中我们也许会想到一个问题——如果变量或者资源他不是独占的,而是共享的(比如对于全局变量的修改),那么如果多个线程同时访问就会引起不可预料的错误

    这个时候就必须要给线程进行加锁确保只能有一个线程运行此函数。

    mutex锁的概念

    Mutex(互斥锁)是一种线程同步机制,用于保护共享资源的访问,防止多个线程同时访问和修改同一份数据而引发竞争条件(race condition)。

    Mutex 的作用是在关键代码段前后加锁和解锁操作,确保只有一个线程能够进入临界区(critical section)执行代码,从而保证共享资源的安全访问。

    同一时刻,同一临界区,只能有一个线程持有该锁

    mutex的使用教程

    锁的声明以及命名

    开头必然要声明库函数

    #include 

    和其他类型的变量一样,之后锁还需要声明一个变量

    mutex mtx_1;

    这个最好是在全局变量中进行声明

    mutex的加锁以及解锁

    在你写函数需要加锁时你只需要调用他们当中的lock(),以及unlck(),如果在执行lock时候如果锁已经被其他线程获取了,那么线程会进行等待

    拿上面的进行举例就是

    1. mtx_1.lock();//加锁
    2. mtx_1.unlock();//解锁

    例子

    运行一个

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. mutex mtx_1;
    8. void F_1(int i) {
    9. mtx_1.lock();
    10. cout << "This is NO." << i << " project is runing." << endl;
    11. this_thread::sleep_for(chrono::seconds(i));
    12. cout << "This is NO." << i << " project is finishing." << endl;
    13. mtx_1.unlock();
    14. }
    15. int main() {
    16. clock_t now_time_1 = clock();
    17. cout << "This project is start!" << endl;//记录刚刚开始的时间
    18. vectorsum_1;
    19. for (int i = 1; i <= 3; i++) {
    20. sum_1.push_back(thread(F_1, i));
    21. }
    22. for (int i = 0; i <= sum_1.size() - 1; i++) {
    23. sum_1[i].join();
    24. }
    25. cout << "This project is ready!" << endl;//记录结束的时间
    26. clock_t now_time_2 = clock();
    27. cout << "The cost time is " << now_time_2 - now_time_1 << " ms " << endl;
    28. return 0;
    29. }

    简单的代码

    结果

    可能有人就要问,这不和之前顺序执行的时间一样吗?

    先不要急,这只是举一个例子,例子也比较极端开头就锁上了,事实上你只需要在有资源冲突的函数部分加锁即可,其他的地方依旧可以和以前一样,甚至不同的函数你可以命名两个锁分别进行执行加锁或者是解锁。

    换言之,锁只是在你需要确保该资源变量在同一时刻只被一个线程访问时加上即可。

    注意

    需要注意的是需要避免的是:两个或者多个线程之间所需要的资源被另外的线程锁住,从而造成死锁。

    mutex的其他方式的锁介绍

    lock_guard

    介绍

    lock_guard是模板类,对比于mutex的区别是lock_guard在创建时会尝试获得锁的所有权(注意时尝试,如果获取不到就相当于没有用,并且不会报错),在作用域结束时会自动析构,无需手动解锁

    该类不可中途上锁和解锁,不可复制

    例子

    还是之前的代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. mutex mtx_1;
    8. void F_1(int i) {
    9. lock_guardguard_1(mtx_1);
    10. cout << "This is NO." << i << " project is runing." << endl;
    11. this_thread::sleep_for(chrono::seconds(i));
    12. cout << "This is NO." << i << " project is finishing." << endl;
    13. }
    14. int main() {
    15. clock_t now_time_1 = clock();
    16. cout << "This project is start!" << endl;//记录刚刚开始的时间
    17. vectorsum_1;
    18. for (int i = 1; i <= 3; i++) {
    19. sum_1.push_back(thread(F_1, i));
    20. }
    21. for (int i = 0; i <= sum_1.size() - 1; i++) {
    22. sum_1[i].join();
    23. }
    24. cout << "This project is ready!" << endl;//记录结束的时间
    25. clock_t now_time_2 = clock();
    26. cout << "The cost time is " << now_time_2 - now_time_1 << " ms " << endl;
    27. return 0;
    28. }

    运行结果

    他并不需要解锁和解锁 

    adopt_lock参数

    adopt_lock用法为

    lock_guardguard_1(mtx_1,adopt_lock);

     加了这个参数,就可以在创建时候不上锁,代表表示这个互斥量已经lock();优化代码的运行时间,同时这个参数本质时起到一个标记

    但是需要注意由于lock_guard不可以主动上锁,如果这个锁本身还没有lock过就会报错。

    unique_lock

    介绍

    unique_lock的用法和lock_guard的用法类似,主要的区别在于他可以中途上锁以及解锁

    对比于lock_guard会更加的灵活

    但是所需要的内存空间会更大

    同时它的也有adopt_lock参数用法一样,而且他还拥有其他的第二参数

    try_to_lock

    他会尝试的去获取锁,如果锁没有被占用就会获取到,如果已经被占用了也会立即放回执行下面的代码不会进行堵塞,用法和adopt_lock一样

    defer_lock

    创建锁的时候不上锁(需要注意区分前面的adopt_lock()这个时没上锁的前提下(如果上锁了会报错)创建该锁时不上锁。之后再进行上锁。),用法也和adopt_lock一样

    release

    为释放unique_lock的所有权,注意是释放——release!!!!不是解锁——unlock,之后的锁需要你自己来管理

    例子

    还是之前的代码中的函数

    1. void F_1(int i) {
    2. unique_lockguard_1(mtx_1);
    3. mutex* mtx_2 = guard_1.release();
    4. cout << "This is NO." << i << " project is runing." << endl;
    5. this_thread::sleep_for(chrono::seconds(i));
    6. cout << "This is NO." << i << " project is finishing." << endl;
    7. mtx_2->unlock();
    8. }
    结果

     当然还是一样的

    总结

    本章讲解了mutex大部分的知识点,使用时需要注意锁住的代码要尽可能的少而精准,这样程序的运行时间和稳定性以及安全性才可以同时得到显著的提升。

  • 相关阅读:
    温馨浪漫HTML表白爱情源码大气简洁单页源码(html生日快乐网站制作)
    python字符串
    dart系列之:集合使用最佳实践
    【Linux】——Linux 常用命令及权限
    vue3项目,vite+vue3+ts+pinia(10)-elementplus布局
    基于32单片机的多功能电子语音时钟
    Java:继承和多态
    机器学习之前的环境准备
    WPF TextBox长文本模式
    华为累计报考PMP超万人次,他们为什么要考呢?
  • 原文地址:https://blog.csdn.net/mumuemhaha/article/details/133554220