c++11线程

dr526

c++11线程基本用法

  • 简单示例

    1. thread和join()

      #include<iostream>
      #include<thread>
      using namespace std;
      
      void test() {
          cout << "我的线程执行" << endl;
          for (int i = 0; i < 10; ++i);
          cout << "我的线程执行完毕" << endl;
      }
      int main() {
          //thread是标准库的类,test为可调用对象,作为线程执行起点
          thread th(test);
          //join():阻塞主线程,让主线程等待子线程执行完毕
          th.join();
          cout << "主线程执行结束" << endl;
          return 0;
      }

      在这里插入图片描述

    2. detach()

      //传统多线程程序主线程要等待子线程执行完毕后才能退出
      //detach():将主线程和子线程分离。可以让主线程不必等待子线程
      //一旦detach()之后,与主线程关联的thread对象就会视区和主线程的关联,此时这个子线程就会在后台运行。当子线程执行完成后,由语和女性时库负责清理该线程相关资源
      //detach()会让子线程失去我们的控制,使用detach()后不能join()
      #include<iostream>
      #include<thread>
      using namespace std;
      
      void test() {
          cout << "我的线程执行" << endl;
          for (int i = 0; i < 10; ++i);
          cout << "我的线程执行完毕" << endl;
      }
      int main() {
          thread th(test);
          th.detach();
          cout << "主线程执行完毕" << endl;
          return 0;
      }

      在这里插入图片描述

 主线程执行完毕后,子线程由运行时库接管,无法打印出来
    1. joinable()

      //joinable():判断是否可以成功join()或者detach(),返回true或者false
      #include<iostream>
      #include<thread>
      using namespace std;
      
      void test() {
          cout << "我的线程执行" << endl;
          for (int i = 0; i < 10; ++i);
          cout << "我的线程执行完毕" << endl;
      }
      int main() {
          thread th(test);
          if (th.joinable()) {
              cout << "1:joinable()==true" << endl;
          }
          else {
              cout << "1:joinable()==false" << endl;
          }
          th.detach();
          if (th.joinable()) {
              cout << "2:joinable()==true" << endl;
          }
          else {
              cout << "2:joinable()==false" << endl;
          }
          cout << "主线程执行完毕" << endl;
          return 0;
      }

      在这里插入图片描述

    • 其他创建线程的方法

      1. 类对象作为可调用对象

        #include<iostream>
        #include<thread>
        using namespace std;
        
        class Test {
        public:
            void operator()() {//不能带参数
                cout << "我的线程开始执行" << endl;
                for (int i = 0; i < 10; ++i);
                cout << "我的线程执行完毕" << endl;
            }
        };
        
        int main() {
            Test test;
            thread th(test);
            th.join();
            cout << "主线程执行完毕" << endl;
            return 0;
        }

        若把join()改成detach(),主线程执行结束,test还在吗?

        如果对象不在了,线程还能继续执行吗?

        这个对象实际上是被复制到线程中去,执行完线程后,test会被销毁,但是复制的对象还在

        #include<iostream>
        #include<thread>
        using namespace std;
        
        class Test {
        public:
            Test(){
                cout << "Test()构造函数被执行" << endl;
            }
            Test(const Test& test) {
                cout << "拷贝构造函数执行" << endl;
            }
            ~Test() {
                cout << "析构函数执行" << endl;
            }
            void operator()() {//不能带参数
                cout << "线程开始执行" << endl;
                for (int i = 0; i < 10; ++i);
                cout << "线程执行完毕" << endl;
            }
        };
        
        int main() {
            Test test;
            thread th(test);
            th.detach();
            for (int i = 0; i < 10; ++i) {
                cout << "主线程执行完毕" << i << endl;
            }
            return 0;
        }

        在这里插入图片描述

     将detach()改为join()后
    
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201006200716787.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMzI2NzQ0,size_16,color_FFFFFF,t_70#pic_center)
    
    
    1. 用lambda表达式

      #include<iostream>
      #include<thread>
      using namespace std;
      
      
      int main() {
          auto test = [] {
              cout << "线程开始执行" << endl;
              for (int i = 0; i < 10; ++i);
              cout << "线程执行完毕" << endl;
          };
          thread th(test);
          th.join();
          cout << "主线程执行完毕"  << endl;
          return 0;
      }
    • 传递临时对象作为线程参数

      1. 可调用对象带有参数时

        #include<iostream>
        #include<thread>
        using namespace std;
        
        //传入的不是myNum的引用,指向地址不同,实际是值传递,即便主线程detach(),子线程使用num不会出问题
        //传入的指针依旧是指向muBuf的地址,所以detach后,子线程会出问题。可以将char*改为const string&
        //使用const string&也有问题,即:不知道mybuf不知道什么时候转换为string,如果主线程执行完毕后,还没转换完毕,就有问题
        void test(const int& num, char* buf) {
            cout << "线程开始执行" << endl;
            cout << num << endl;
            cout << buf << endl;
            cout << "线程执行完毕" << endl;
        }
        
        int main() {
            int myNum = 1;
            int& num = myNum;
            char myBuf[] = "only for test!";
            thread th(test, num, myBuf);
            th.join();
            cout << "主线程执行完毕" << endl;
            return 0;
        }
        // 生成一个临时string对象,绑定const string&
        //使用这个方法,就会让string对象在主线程执行完毕前构造
        //在创建线程的同时构造临时对象是可行的
        thread th(test,num,string(myBuf));

        总结(针对detach())

        (1)若传递int这种简单类型参数,建议都是值传递,不要用引用

        (2)如果传递类对象,避免隐式类型转换。全部都要在创建线程就构建临时对象,然后函数参数中用引用,否则系统还会构造一次

      2. 线程id的概念

        (1)每个线程(包括主线程)实际都对应着一个数字,并且数字都不同

        (2)线程id可以用c++标准库中的函数来获取,即:std::this_thread::get_id()

      3. 临时对象构造时机抓捕

        #include<iostream>
        #include<thread>
        using namespace std;
        
        class A {
        public:
            int num;
            //类型转换构造函数,可以把一个int转换成一个类A对象
            A(int num) :num(num) {
                cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
            A(const A& a) :num(a.num) {
                cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
            ~A() {
                cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
        };
        
        void test(const A& a) {
            cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
        }
        
        int main() {
            cout << "主线程id是" << std::this_thread::get_id() << endl;
            int num = 2;
            thread th(test, num);
            th.join();
            cout << "主线程执行完毕" << endl;
            return 0;
        }

        在这里插入图片描述

     A类对象在子线程中构造的,若改为detach()后,当主线程执行完毕后,还没构造,就出现问题了
    
     ```c++
     //在创建线程就构建临时对象
     thread th(test, A(num));
     ```
    
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201006200754483.png#pic_center)
    
    
    • 传递类对象、智能指针作为线程参数

      1. 线程参数为应用时修改值,不会影响主线程的值

        #include<iostream>
        #include<thread>
        using namespace std;
        
        class A {
        public:
            mutable int num;//标注为const也能修改
            //类型转换构造函数,可以把一个int转换成一个类A对象
            A(int num) :num(num) {
                cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
            A(const A& a) :num(a.num) {
                cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
            ~A() {
                cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
            }
        };
        
        void test(const A& a) {
            a.num = 199;//修改不会影响main的值
            cout << "a.num=" << a.num << endl;
            cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
        }
        
        int main() {
            cout << "主线程id是" << std::this_thread::get_id() << endl;
            A a(10);
            thread th(test, a);//将类对象作为线程参数
            th.join();
            cout << "主线程的a.num=" << a.num << endl;
            cout << "主线程执行完毕" << endl;
            return 0;
        }

        在这里插入图片描述

      2. std::ref函数,可以让线程传入的参数不被复制(注:没使用detach())

        //将线程传入参数加上std::ref
        thread th(test, std::ref(a));

        在这里插入图片描述

      3. 传递智能指针

        #include<iostream>
        #include<thread>
        using namespace std;
        
        void test(unique_ptr<int> uptr) {
            cout << "子线程id是" << std::this_thread::get_id() << endl;
            cout << "当前智能指针的地址是" << uptr << endl;
        }
        
        int main() {
            cout << "主线程id是" << std::this_thread::get_id() << endl;
            unique_ptr<int> uptr(new int(10));
            cout << "当前智能指针的地址为" << uptr << endl;
            thread th(test, std::move(uptr));//将类对象作为线程参数
            th.join();
            cout << "主线程执行完毕" << endl;
            return 0;
        }

        在这里插入图片描述

     若改为detach(),当主线程执行完毕后,uptr被释放,但子线程还在执行,此时就会出现问题
    
    • 用成员函数指针做线程函数

      #include<iostream>
      #include<thread>
      using namespace std;
      
      class A {
      public:
          mutable int num;
          //类型转换构造函数,可以把一个int转换成一个类A对象
          A(int num) :num(num) {
              cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
          }
          A(const A& a) :num(a.num) {
              cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
          }
          ~A() {
              cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
          }
          void thread_work(int num) {
              cout << "子线程thread_work执行了" << ",threadid=" << std::this_thread::get_id() << endl;
          }
      };
      
      void test(const A& a) {
          a.num = 199;//修改不会影响main的值
          cout << "a.num=" << a.num << endl;
          cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
      }
      
      int main() {
          cout << "主线程id是" << std::this_thread::get_id() << endl;
          A a(10);
          //第一个参数是对象函数指针,第二个参数对象,其后参数为函数所需参数
          thread th(&A::thread_work, a, 1);
          th.join();
          cout << "主线程的a.num=" << a.num << endl;
          cout << "主线程执行完毕" << endl;
          return 0;
      }

    c++11线程的互斥量

    • 创建和等待多个线程

      #include<iostream>
      #include<thread>
      #include<vector>
      using namespace std;
      
      
      //线程的入口函数
      void test(int num) {
          cout << "线程开始执行,线程编号=" << num << endl;
          for (int i = 0; i < 10; ++i);
          cout << "线程执行结束了,线程编号=" << num << endl;
      }
      
      int main() {
          cout << "主线程开始执行" << endl;
          //创建和等待多个线程
          vector<thread> threads;
          //创建10个线程,线程入口统一使用test
          for (int i = 0; i < 10; ++i) {
              threads.push_back(thread(test, i));//创建10个线程,同时这10个线程开始执行
          }
          for (auto i = threads.begin(); i != threads.end(); ++i) {
              i->join();//等待10个线程返回
          }
          cout << "主线程执行结束" << endl;
          return 0;
      }

      在这里插入图片描述

    表明线程的执行是无序的,和操作系统内部对线程的调度有关

    • 数据共享问题分析——mutex

      1. 只读的数据

        #include<iostream>
        #include<thread>
        #include<vector>
        using namespace std;
        
        int global = 10;//共享数据
        
        //线程的入口函数
        void test(int num) {
            cout << "编号为" << std::this_thread::get_id() << "打印global_v的值" << global << endl;
            for (int i = 0; i < 10; ++i);
            cout << "线程执行结束了,线程编号=" << std::this_thread::get_id() << endl;
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建和等待多个线程
            vector<thread> threads;
            //创建10个线程,线程入口统一使用test
            for (int i = 0; i < 10; ++i) {
                threads.push_back(thread(test, i));//创建10个线程,同时这10个线程开始执行
            }
            for (auto i = threads.begin(); i != threads.end(); ++i) {
                i->join();//等待10个线程返回
            }
            cout << "主线程执行结束" << endl;
            return 0;
        }

        在这里插入图片描述

     所有线程读取的数据都相等,即:**只读数据安全稳定**
    
    1. 有读有写

      #include<iostream>
      #include<thread>
      using namespace std;
      
      int num[10] = { 0 };
      
      void write() {
          for (int i = 0; i < 10; ++i) {
              num[i] = 1;
              this_thread::sleep_for(std::chrono::milliseconds(1));
          }
      }
      
      void read() {
          for (int i = 1; i < 10; ++i) {
              if (num[i] != num[i - 1]) {
                  cout << "数据不一致" << endl;
              }
          }
      }
      
      int main() {
          cout << "主线程开始执行" << endl;
          //创建2个线程,一个线程负责读,一个负责写
          thread write(write);
          thread read(read);
          write.join();
          read.join();
          cout << "主线程执行结束" << endl;
          return 0;
      }

      在这里插入图片描述

    • 共享数据的保护

      1. 互斥量的基本概念

        类的对象,可以理解为一把锁,多个线程尝试用lock()成员函数来加锁这把锁头,只有一个线程可以锁定成功(成功的标志是lock()函数返回),如果没有锁定成功,线程就会阻塞在这里,不断尝试加锁

      2. 互斥量的用法

        1. 先lock(),操作共享数据,unlock()
        2. lock()和unlock()要成对使用,lock()必然要有unlock,每调用一次lock(),必然要调用一次unlock()
        3. 注意:mutex是不可复制对象,所以mutex作为类的成员要注意。C++中thread调用“带mutex的类”的成员函数报错C2661:std::tuple解决方法_tomwillow的博客-CSDN博客
        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        int num[10] = { 0 };
        
        void write() {
            int copy[10];
            for (int i = 0; i < 10; ++i) {
                copy[i] = 1;
                this_thread::sleep_for(std::chrono::milliseconds(1));
            }
            g_mutex.lock();
            memcpy(num, copy, 10);
            g_mutex.unlock();
        }
        
        void read() {
            int copy[10] = { 0 };
            g_mutex.lock();
            memcpy(copy, num, 10);
            g_mutex.unlock();
            for (int i = 1; i < 10; ++i) {
                if (copy[i] != copy[i - 1]) {
                    cout << "数据不一致" << endl;
                }
            }
            
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建2个线程,一个线程负责读,一个负责写
            thread write(write);
            thread read(read);
            write.join();
            read.join();
            cout << "主线程执行结束" << endl;
            return 0;
        }
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020101311253696.png#pic_center)
    
     4. 为了防止忘记unlock(),引入一个叫**std::lock_guard**的类模板。类似于智能指针
    
        (1)std::lock_guard直接取代了lock()和unlock()
    
        (2)lock_guard构造函数中执行了lock(),析构函数中执行了unlock()
    
        ```c++
        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        int num[10] = { 0 };
        
        void write() {
            int copy[10];
            for (int i = 0; i < 10; ++i) {
                copy[i] = 1;
                this_thread::sleep_for(std::chrono::milliseconds(1));
            }
            std::lock_guard<mutex> write_mutex(g_mutex);
            memcpy(num, copy, 10);
        }
        
        void read() {
            int copy[10] = { 0 };
            std::lock_guard<mutex> read_mutex(g_mutex);
            memcpy(copy, num, 10);
            for (int i = 1; i < 10; ++i) {
                if (copy[i] != copy[i - 1]) {
                    cout << "数据不一致" << endl;
                }
            }
            
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建2个线程,一个线程负责读,一个负责写
            thread write(write);
            thread read(read);
            write.join();
            read.join();
            cout << "主线程执行结束" << endl;
            return 0;
        }
        ```
    
        如果不想线程执行完毕后std::lock_guard才执行析构函数,可以将其和需要加锁的共享数据放到{}中
    
        ```c++
        {
            std::lock_guard<mutex> write_mutex(g_mutex);
            memcpy(num, copy, 10);
        }
        //此时std::lock_guard属于{}这个作用域,当在{}外后,std::lock_guard生命周期结束,就会析构,即执行unlock
        ```
    
    1. 死锁

      1. 死锁的前提是至少有两个互斥量(锁头)才能产生
      2. 目前有两个互斥量,即:锁1,锁2,两个线程A,B

        (1)线程A执行的时候,这个线程先锁锁1,把锁1锁成功了,然后它去锁锁2,此时出现上下文切换,线程A让出CPU

        (2)线程B执行了,这个线程先锁锁2,因为锁2没有被锁,所以锁2锁成功,然后它去锁锁1

        (3)此时死锁产生,线程A拿不到锁2,解不开锁1,线程B拿不到锁1,解不开锁2,两个线程就无限互相等待下去

      3. 死锁的一般解决办法:保证两个互斥量的lock()顺序一致
      4. std::lock()函数模板

        1. 一次锁住两个或两个以上的互斥量(1个不行),它就不会存在多个线程因为锁的顺序导致的死锁风险。
        2. 如果互斥量中有一个没锁住,它就会一直等待,直到所有的互斥量都锁住
        3. 要么多个互斥量都锁住,要么都没有锁住。如果只锁一个其他没成功,它就会立即把已经lock()的都unlock()

          mutex g_mutex1;
          mutex g_mutex2;
          std::lock(g_mutex1,g_mutex2);
          g_mutex1.unlock();
          g_mutex2.unlock();
        4. std::lock_guard的std::adopt_lock

          //是一个结构体对象,其一个标记作用,表示互斥量已经lock()
          //std::adopt_lock让lock_guard不执行lock()
          mutex g_mutex1;
          mutex g_mutex2;
          std::lock(g_mutex1,g_mutex2);
          std::lock_guard<mutex> guard1(g_mutex1,std::adopt_lock);
          std::lock_guard<mutex> guard2(g_mutex2,std::adopt_lock);
      5. unique_lock

        (1)unique_lock是个类模板,和lock_guard类似,都是对mutex进行lock()和unlock()操作的

        (2)unique_lock比lock_guard更灵活,但效率上差一点,内存占用多一点。

        (3)用法和lock_guard类似

        mutex g_mutex;
        std::unique_lock<std::mutex> uniqueLock(g_mutex);

        (4)unique_lock第二个参数

        ​ a. std::adopt_lock(lock_guard也可使用):表示互斥量已经lock(),即:不需要在unique_lock的构造函数进行lock()

        ​ b. std::try_to_lock:会尝试mutex的lock()去锁定mutex,但如果没有锁定成功,就会立即返回,并不会阻塞(前提是使用try_to_lock前不能单独使用lock())

        std::unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock);
        if(uniqueLock.owns_lock()){//尝试Lock()成功
            
        }
        
        
        //实例
        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        int num[10] = { 0 };
        
        void write() {
            int copy[10];
            for (int i = 0; i < 10; ++i) {
                copy[i] = 1;
            }
            unique_lock<mutex> uniqueLock(g_mutex);
            this_thread::sleep_for(chrono::milliseconds(2000));
            memcpy(num, copy, 10);
        }
        
        void read() {
            int copy[10] = { 0 };
            unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock);
            if (uniqueLock.owns_lock()) {
                memcpy(copy, num, 10);
            }
            else {
                cout << "线程2没有拿到锁" << endl;
            }
            for (int i = 1; i < 10; ++i) {
                if (copy[i] != copy[i - 1]) {
                    cout << "数据不一致" << endl;
                }
            }
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建2个线程,一个线程负责读,一个负责写
            thread write(write);
            thread read(read);
            write.join();
            read.join();
            cout << "主线程执行结束" << endl;
            return 0;
        }

        在这里插入图片描述

        ​        c. **std::defer_lock**:并没有给mutex,即:**初始化一个没有lock()的mutex**(defer_lock的前提是不能先lock()),经常配合unique_lock的成员函数使用
    
     6. unique_lock的重要成员函数
    
        (1)lock()
    
        (2)unlock():unique_lock也可以自动解锁
    
        (3)try_lock():(类似于try_to_lock)尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,这个函数是**不阻塞**的
    
        ```c++
        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        int num[10] = { 0 };
        
        void write() {
            int copy[10];
            for (int i = 0; i < 10; ++i) {
                copy[i] = 1;
            }
            unique_lock<mutex> uniqueLock(g_mutex);
            this_thread::sleep_for(chrono::milliseconds(2));
            memcpy(num, copy, 10);
        }
        
        void read() {
            int copy[10] = { 0 };
            unique_lock<mutex> uniqueLock(g_mutex,defer_lock);
            if (uniqueLock.try_lock() == true) {
                memcpy(copy, num, 10);
            }
            else {
                cout << "线程2没有拿到锁" << endl;
            }
            for (int i = 1; i < 10; ++i) {
                if (copy[i] != copy[i - 1]) {
                    cout << "数据不一致" << endl;
                }
            }
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建2个线程,一个线程负责读,一个负责写
            thread write(write);
            thread read(read);
            write.join();
            read.join();
            cout << "主线程执行结束" << endl;
            return 0;
        }
        ```
    
        ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201013112643439.png#pic_center)
    
    
        (4)release():返回它所管理的mutex对象指针,并**释放所有权**,也就是说,这个unique_lock和mutex不再联系(unlock不会释放所有权)。
    
        ```c++
        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        int num[10] = { 0 };
        
        void write() {
            int copy[10];
            for (int i = 0; i < 10; ++i) {
                copy[i] = 1;
            }
            unique_lock<mutex> uniqueLock(g_mutex);
            mutex* ptx = uniqueLock.release();//释放mutex所有权,所以需要自己unlock()
            memcpy(num, copy, 10);
            ptx->unlock();
        }
        
        void read() {
            int copy[10] = { 0 };
            unique_lock<mutex> uniqueLock(g_mutex);
            memcpy(copy, num, 10);
            for (int i = 1; i < 10; ++i) {
                if (copy[i] != copy[i - 1]) {
                    cout << "数据不一致" << endl;
                }
            }
        }
        
        int main() {
            cout << "主线程开始执行" << endl;
            //创建2个线程,一个线程负责读,一个负责写
            thread write(write);
            thread read(read);
            write.join();
            read.join();
            cout << "主线程执行结束" << endl;
            return 0;
        }
        ```
    
     7. unique_lock所有权
    
        ```c++
        mutex g_mutex;
        unique_lock<mutex> uniqueLock(g_mutex);//uniqueLock拥有g_mutex的所有权
        //不能复制所有权,但是所有权可以转移
        
        //转移方法1
        unique_lock<mutex> uniqueLock_(move(uniqueLock));//当前uniqueLock失去g_mutex控制权,指向空,uniqueLock_指向g_mutex
        
        //转移方法2
        //从函数返回一个局部的unique_lock对象是允许的
        //返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
        unique_lock<mutex> rtn_unique_lock(){
            unique_lock<mutex> uniqueLock(g_mutex);
            return uniqueLock;
        }
        ```
    
    • 单例设计模式共享数据分析、解决,call_once

      1. 单例设计模式

        (1)单例:整个项目中,有某个或者某些特殊的类,并且只能创建一个属于该类的对象

        (2)单例类示例

        #include<iostream>
        using namespace std;
        
        class MyCAS {//这是一个单例类
        private:
            MyCAS(){}//私有化构造函数
        private:
            static MyCAS* m_instance;//静态成员变量
        public:
            static MyCAS* getInstance() {
                if (m_instance == nullptr) {
                    m_instance = new MyCAS();
                    static Release release;//当程序退出时执行析构函数
                }
                return m_instance;
            }
            class Release {//用来释放对象
            public:
                ~Release() {
                    if (MyCAS::m_instance!=nullptr) {
                        delete m_instance;
                        MyCAS::m_instance = nullptr;
                    }
                }
            };
        };
        //类静态变量初始化
        MyCAS* MyCAS::m_instance = nullptr;
        
        int main() {
            MyCAS* ptr = MyCAS::getInstance();//创建一个对象,返回该类(对象)指针
            MyCAS* ptr_ = MyCAS::getInstance();
            cout << "ptr=" << ptr << endl;
            cout << "ptr_=" << ptr_ << endl;
            return 0;
        }

        在这里插入图片描述

      2. 单例设计模式共享数据问题分析、解决

        问题:在创建的线程(非主线程)中创建单例类对象,并且这种线程可能不止一个,就需要将getInstance()这种成员函数互斥

        原因:当第一个线程在new之前失去cpu,第二个线程就会执行new一次,此时第一个线程再次执行,就又会new一次,这就不符合单例设计模式了

        示例:

        #include<iostream>
        #include<thread>
        #include<mutex>
        using namespace std;
        
        mutex g_mutex;
        
        class MyCAS {//这是一个单例类
        private:
            MyCAS(){}//私有化构造函数
        private:
            static MyCAS* m_instance;//静态成员变量
        public:
            static MyCAS* getInstance() {
                //提高效率,只有第一次没初始化时会加锁
                if (m_instance == nullptr) {//双重锁定(双重检查)
                    unique_lock<mutex> lock(g_mutex);//自动加锁
                    if (m_instance == nullptr) {
                        m_instance = new MyCAS();
                        static Release release;//当程序退出时执行析构函数
                    }
                }
                return m_instance;
            }
        
            class Release {//用来释放对象
            public:
                ~Release() {
                    if (MyCAS::m_instance!=nullptr) {
                        delete m_instance;
                        MyCAS::m_instance = nullptr;
                    }
                }
            };
        
            void fun() {
                cout << "测试" << endl;
            }
        };
        //类静态变量初始化
        MyCAS* MyCAS::m_instance = nullptr;
        
        void thread1() {
            cout << "线程th1开始执行" << endl;
            MyCAS* ptr = MyCAS::getInstance();
            cout << "线程th1执行完毕" << endl;
        }
        
        void thread2() {
            cout << "线程th2开始执行" << endl;
            MyCAS* ptr = MyCAS::getInstance();
            cout << "线程th2执行完毕" << endl;
        }
        
        int main() {
            thread th1(thread1);
            thread th2(thread2);
            th1.join();
            th2.join();
            return 0;
        }
      3. std::call_once()——c++11标准

        函数的第二个参数是一个函数名,需要和std::once_flag标记结合使用

        功能:保证函数只被调用一次,即:通过std::once_flag这个标记来判断函数是否执行,当调用call_once()成功后,call_once()就把这个标记设置为一种已调用状态。后续再次调用call_once(),只要once_falg被设置为已调用,就不会执行了

        具备互斥量的能力

        #include<iostream>
        #include<thread>
        #include <mutex>
        using namespace std;
        
        once_flag g_flag;//这是系统定义的标记
        
        class MyCAS {//这是一个单例类
            static void CreateInstance() {//只被调用一次
                m_instance = new MyCAS();
                static Release release;//当程序退出时执行析构函数
            }
        private:
            MyCAS(){}//私有化构造函数
        private:
            static MyCAS* m_instance;//静态成员变量
        public:
            static MyCAS* getInstance() {
                //两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕CreateInstance后,就会放弃执行CreateInstance
                call_once(g_flag, CreateInstance);
                return m_instance;
            }
        
            class Release {//用来释放对象
            public:
                ~Release() {
                    if (MyCAS::m_instance!=nullptr) {
                        delete m_instance;
                        MyCAS::m_instance = nullptr;
                    }
                }
            };
        
            void fun() {
                cout << "测试" << endl;
            }
        };
        //类静态变量初始化
        MyCAS* MyCAS::m_instance = nullptr;
        
        void thread1() {
            cout << "线程th1开始执行" << endl;
            MyCAS* ptr = MyCAS::getInstance();
            cout << "线程th1执行完毕" << endl;
        }
        
        void thread2() {
            cout << "线程th2开始执行" << endl;
            MyCAS* ptr = MyCAS::getInstance();
            cout << "线程th2执行完毕" << endl;
        }
        
        int main() {
            thread th1(thread1);
            thread th2(thread2);
            th1.join();
            th2.join();
            return 0;
        }

        # c++11线程的条件变量

    • 轮询机制:每隔一定时间,进行查询

      缺点:查询不能太频繁(浪费CPU),也不能太不频繁(缓冲区满),难以把握。性能不佳

      #include <iostream>
      #include<thread>
      #include<mutex>
      using namespace std;
      
      mutex g_mutex;
      int g_buf[100];//缓冲区,最多存放一百个数
      int g_count = 0;
      
      //第一个线程:生产者
      void Producer() {
          while (true) {
              int r = rand() % 20 + 1;//生成一个1……20之间的随机数
              this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间
              //存放一个物品(这里存放的数据代表物品)
              g_mutex.lock();
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
              g_mutex.unlock();
          }
      }
      
      //第二个线程:消费者
      void Consume() {
          while (true) {
              this_thread::sleep_for(chrono::milliseconds(50));
              g_mutex.lock();
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
              g_mutex.unlock();
          }
      }
      
      int main()
      {
          srand(time(nullptr));
          thread producer(Producer);
          thread consume(Consume);
          producer.join();
          consume.join();
          return 0;
      }

      在这里插入图片描述

    • 条件变量——std::condition_variable

      (1)std::condition_variable实际是一个类,是一个和条件相关的一个类,即:等待一个条件达成。

      (2)这个类是需要和互斥量来配合工作,用的时候我们要生成这个类的对象

      (3)wait()

      ​ a. 如果第二个参数(可调用对象)返回的值是false,那么wait()将解锁互斥量,并堵塞到 本行。堵塞到其他某个线程调用notify_one()成员函数为止。如果第二个参数返回值是true,那么wait()会直接返回

      ​ b. 如果wait()没有第二个参数,那么就和第二个参数返回false效果一样。

      (4)notify_one()

      ​ (1)尝试把一个wait()的线程唤醒,如果没有wait()线程,那么notify_one()就没效果,但是不会阻塞在notify_one()这里

      ​ (2)当其他线程用notify_one()将wait()【原本阻塞】,wait()就开始恢复干活了,即

      ​ a. 不断尝试重新获取互斥量锁,如果获取不到,线程就阻塞在wait()这里等着获取锁。 b. 如果获取到就继续执行;获取到锁就执行lock()。

      ​ 如果wait有第二个参数,就判断这个参数返回值,如果返回值为false,wait()又会对互斥量解锁,并再次阻塞,等待notify_one()唤醒

      ​ 如果wait没有第二个参数,则线程继续执行

      (5)示例

      #include <iostream>
      #include<thread>
      #include<mutex>
      using namespace std;
      
      mutex g_mutex;
      condition_variable g_cond;//生成一个条件变量对象
      int g_buf[100];//缓冲区,最多存放一百个数
      int g_count = 0;
      
      //第一个线程:生产者
      void Producer() {
          while (true) {
              int r = rand() % 20 + 1;//生成一个1……20之间的随机数
              this_thread::sleep_for(chrono::milliseconds(50 * r));//避免本线程notify_one()后比wait()先拿到锁,休息时间在50-1000毫秒之间
              //存放一个物品(这里存放的数据代表物品)
              unique_lock<mutex> lock(g_mutex);
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
              g_cond.notify_one();
          }
      }
      
      //第二个线程:消费者
      void Consume() {
          while (true) {
              unique_lock<mutex> lock(g_mutex);
              g_cond.wait(lock, [&] {//lambda为可调用对象
                  if (g_count > 0)
                      return true;
                  return false;
                  });
              for (int i = 0; i < g_count; ++i) {
                  cout << "消耗物品:" << g_buf[i] << endl;
              }
              g_count = 0;
          }
      }
      
      int main()
      {
          srand(time(nullptr));
          thread producer(Producer);
          thread consume(Consume);
          producer.join();
          consume.join();
          return 0;
      }
    • 上述代码深入思考

      (1) 当删除this_thread::sleep_for(chrono::milliseconds(50 * r))后,放入和消耗就可能不会交替执行,因为notify_one()后,线程可能先于wait()拿到锁

      在这里插入图片描述

    (2)注意:notify_one()不一定起作用,但不会阻塞。因为执行notify_one()时,如果没有其他线程wait(),它就没有效果

    • notify_all()

      当程序中有多个线程使用wait()时,使用notify_one()只会在某时刻唤醒其中任意一个线程,另外其他线程依然在阻塞中,即:任意时刻只有一个wait()尝试拿锁,其他都在阻塞中

      使用notify_all()会将所有wait()的线程都唤醒,所有线程的wait()都会尝试拿锁

    c++11 async、future、packaged_task、promise

    • std::asyc、std::future创建后台任务并返回值

      1. std::asyc:是一个函数模板,用来启动一个异步任务,启动一个异步任务后,它会返回一个std::future(类模板)对象
      2. 启动一个异步任务:就是自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象
      3. std::future对象里就含有线程入口函数返回的结果(线程返回的结果),可以通过调用future对象的成员函数get()来获取结果
      4. std::future:提供了一种访问异步操作结果的机制,即:这个结果可能无法马上到达,但是不久的将来,当线程执行完毕的时候,就可以拿到结果
      5. 示例

        #include<iostream>
        #include<future>
        using namespace std;
        
        int myThread() {//线程入口函数
            cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(5000));
            cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
            return 5;
        }
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            future<int> result = async(myThread);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行
            cout << "continue……!" << endl;
            cout << result.get() << endl;//主函数会阻塞在这里,等待线程返回
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
        #include<iostream>
        #include<future>
        using namespace std;
        
        class Thread {
        public:
            int myThread(int num) {//线程入口函数
                cout << num << endl;
                cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
                this_thread::sleep_for(chrono::milliseconds(5000));
                cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
                return 5;
            }
        };
        
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            Thread th;
            future<int> result = async(&Thread::myThread,&th,12);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行
            cout << "continue……!" << endl;
            cout << result.get() << endl;//主函数会阻塞在这里,等待线程返回
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
      6. 上述程序通过future对象的get()成员函数等待线程结束并返回结果,即:会阻塞future对象所在的线程,并返回结果
      7. future对象的get()成员函数只能调用一次
      8. future对象还有一个wait()成员函数,该函数只是等待线程结束,本身不会返回结果
      9. 额外向std::async()传递一个参数,该参数类型是std::launch类型(枚举类型),来达到一些特殊的目的

        (1)std::launch::deferred:不会创建新线程,通过future对象调用get()或者wait()函数,就会直接调用async中的可调用对象

        #include<iostream>
        #include<future>
        using namespace std;
        
        class Thread {
        public:
            int myThread(int num) {//线程入口函数
                cout << num << endl;
                cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
                this_thread::sleep_for(chrono::milliseconds(5000));
                cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
                return 5;
            }
        };
        
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            Thread th;
            future<int> result = async(launch::deferred, &Thread::myThread, &th, 12);
            cout << "continue……!" << endl;
            cout << result.get() << endl;
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }

        在这里插入图片描述

     (2)**std::launch::async**:强制这个异步任务在新线程上执行,即:系统必须创建出新线程执行可调用对象
    
     (3)**std::launch::async | std::launch::deferred**:这是**async的默认值**,表示调用async的行为可能是创建新线程并立即执行,或者没有创建新线程并延迟到调用get()或者wait()才开始执行任务入口函数
    
    1. std::async和std::thread的区别:

      (1)std::async不一定会创建新线程执行可调用对象。std::thread会创建新线程,如果系统资源紧张,创建线程失败,那么整个程序就会崩溃

      (2)std::async调用方法可以用简单的get()函数拿到线程可调用对象的返回值

      (3)std::thread创建的线程过多,可能创建失败,系统报告异常,崩溃。而std::async一般不会报告异常、崩溃,因为当系统资源紧张导致无法创建新线程的时候,std::async使用默认值调用时就不会创建新线程,而是后序当某个线程使用get()获取返回值时,就在该线程执行可调用对象

    2. std::async使用默认值

      当std::async使用默认值作为参数时,会产生不确定性【即:是否创建新线程】

      借助future的wait_for()来判断是否创建新线程

      #include<iostream>
      #include<thread>
      #include<atomic>
      #include<future>
      using namespace std;
      
      atomic<int> g_num = 0;
      
      int myThread() {
          cout << "myThread() start, " << "thread_id=" << this_thread::get_id() << endl;
          cout << "myThread() end, " << "thread_id=" << this_thread::get_id() << endl;
          return 1;
      }
      
      int main() {
          cout << "main() start, " << "thread_id=" << this_thread::get_id() << endl;
          future<int> result = async(myThread);
          future_status status = result.wait_for(0s);//等待chrono::seconds(0)
          if (status == future_status::deferred) {
              cout << "没有创建新线程" << endl;
              cout << result.get() << endl;//此时才执行myThread()
          }
          else {
              //创建了新线程
              if (status == future_status::ready) {
                  cout << "线程成功创建" << endl;
                  cout << result.get() << endl;
              }
              else if (status == future_status::timeout) {
                  //超时,线程还没执行完毕
                  cout << "超时,线程还在执行中" << endl;
                  cout << result.get() << endl;
              }
          }
          cout << "main() end, " << "thread_id=" << this_thread::get_id() << endl;
          return 0;
      }
    • std::packaged_task

      1. std::packaged_task:是一个类模板,模板参数是各种可调用对象,通过std::packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数
      2. packaged_task:包装的对象还是可以直接调用的
      3. 可以通过get_future()获取future对象,从而取得线程的返回值
      4. 示例

        #include<iostream>
        #include <future>
        #include<thread>
        using namespace std;
        
        int myThread(int num) {//线程入口函数
            cout << num << endl;
            cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(5000));
            cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
            return 5;
        }
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            packaged_task<int(int)> myFunc(myThread);//将函数myThread通过packaged_task包装起来
            thread th(std::ref(myFunc), 1);
            th.join();
            std::future<int> result = myFunc.get_future();
            cout << result.get() << endl;
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
        #include<iostream>
        #include <future>
        #include<thread>
        using namespace std;
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            packaged_task<int(int)> myFunc([](int num) {
                    cout << num << endl;
                    cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
                    this_thread::sleep_for(chrono::milliseconds(5000));
                    cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
                    return 5;
                });
            thread th(ref(myFunc), 12);
            th.join();
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
        #include<iostream>
        #include <future>
        #include<thread>
        using namespace std;
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            packaged_task<int(int)> myFunc([](int num) {
                    cout << num << endl;
                    cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
                    this_thread::sleep_for(chrono::milliseconds(5000));
                    cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
                    return 5;
                });
            myFunc(12);//直接调用
            future<int> result = myFunc.get_future();
            cout << result.get() << endl;//注意,没有创建新线程,而是触发lambda表达式执行,即:相当于函数调用
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
        #include<iostream>
        #include <future>
        #include<thread>
        #include<vector>
        using namespace std;
        
        vector<packaged_task<int(int)>> vec;
        
        int main() {
            cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
            packaged_task<int(int)> myFunc([](int num) {
                cout << num << endl;
                cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
                this_thread::sleep_for(chrono::milliseconds(5000));
                cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
                return 5;
                });
            vec.push_back(move(myFunc));//这里用到了移动语义,此时myFunc为空
            packaged_task<int(int)> myFunc_ = move(vec.back());
            vec.pop_back();
            myFunc_(123);
            future<int> result = myFunc_.get_future();
            cout << result.get() << endl;
            cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
            return 0;
        }
    • std::promise

      std::promise也是一个类模板,能够在某个线程中给它赋值,然后可以在其他线程中,将这个值取出使用

      #include<iostream>
      #include<thread>
      #include <future>
      using namespace std;
      
      void myThread(promise<int>& temp, int num) {
          //模拟完成一系列复杂操作
          this_thread::sleep_for(chrono::milliseconds(5000));
          temp.set_value(num);//将结果保存到promise对象中
      }
      
      int main() {
          promise<int> myTemp;//声明一个promise对象,保存类型为int
          thread th(myThread, ref(myTemp), 520);
          th.join();
          //获取结果值
          future<int> result = myTemp.get_future();//promise和future绑定,用于获取线程返回值
          cout << result.get() << endl;
          return 0;
      }
      #include<iostream>
      #include<thread>
      #include <future>
      using namespace std;
      
      void myThread(promise<int>& temp, int num) {
          //模拟完成一系列复杂操作
          this_thread::sleep_for(chrono::milliseconds(5000));
          temp.set_value(num);//将结果保存到promise对象中
      }
      
      void myThread_(future<int>& temp) {
          int result = temp.get();
          cout << "myThread_ result=" << result << endl;
      }
      
      int main() {
          promise<int> myTemp;//声明一个promise对象,保存类型为int
          thread th(myThread, ref(myTemp), 520);
          th.join();
          future<int> result = myTemp.get_future();//promise和future绑定,用于获取线程返回值
          thread th_(myThread_, ref(result));
          th_.join();
          return 0;
      }

    future其他成员函数、shared_future、atomic

    • std::future其它成员函数

      wait_for(time):阻塞time时长,若time时长后,线程还没返回,则wait_for返回future_status::timeout。如果在time时长之内,线程成功返回,则wait_for()返回future_status::ready。如果async第一个参数设置为std::launch::deferred,则wait_for()返回future_status::deferred

      #include<iostream>
      #include<future>
      using namespace std;
      
      int myThread() {//线程入口函数
          cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
          this_thread::sleep_for(chrono::milliseconds(1000));
          cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
          return 5;
      }
      
      int main() {
          cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
          future<int> result = async(myThread);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行
          cout << "continue……!" << endl;
          //枚举类型
          future_status status = result.wait_for(chrono::seconds(2));
          if (status == future_status::timeout) {
              cout << "timeout" << endl;
          }
          else if (status == future_status::ready) {
              cout << "成功返回" << endl;
              cout << result.get() << endl;
          }
          else if (status == future_status::deferred) {
              cout << "deferred" << endl;
              cout << result.get() << endl;
          }
          cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
          return 0;
      }
      

      在这里插入图片描述

    • std::shared_future

      std::future只能调用一次get()函数,因为get()函数的设计是一个移动语义

      std::shared_future:也是一个类模板,get()函数是复制数据

      #include<iostream>
      #include <future>
      #include<thread>
      using namespace std;
      
      int myThread() {//线程入口函数
          cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
          this_thread::sleep_for(chrono::milliseconds(5000));
          cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
          return 5;
      }
      
      void myThread_(shared_future<int>& temp) {
          cout << "myThread_() start," << "threadid=" << this_thread::get_id() << endl;
          cout << "myThread_():" << temp.get() << endl;
          cout << "myThread_() end," << "threadid=" << this_thread::get_id() << endl;
      }
      
      int main() {
          cout << "main() start," << "threadid=" << this_thread::get_id() << endl;
          packaged_task<int(void)> myFunc(myThread);//将函数myThread通过packaged_task包装起来
          thread th(std::ref(myFunc));
          th.join();
          shared_future<int> result = myFunc.get_future();
          cout << "main():"<< result.get() << endl;
          thread th_(myThread_, std::ref(result));
          th_.join();
          cout << "main() end," << "threadid=" << this_thread::get_id() << endl;
          return 0;
      }

      在这里插入图片描述

    • std::atomic——原子操作

      原子操作概念及其范例

      (1)原子操作:是指“不可分割的操作”,要么执行成功,要么失败,即:是在多线程中不会打断的程序执行片段

      (2)原子操作:比互斥量的效率更胜一筹

      (3)互斥量加锁一般针对的是一个代码片段,而原子操作针对的一般都是一个变量,而不是一个代码片段

      (4)std::atomic:类模板,用来代表原子操作

      (5)示例

      #include<iostream>
      #include<thread>
      #include<atomic>
      using namespace std;
      
      atomic<int> g_num = 0;
      
      void Write() {
          for (int i = 0; i < 1000000; ++i) {
              //可替换成g_num+=1;
              ++g_num;//操作为原子操作,在多线程中不会被打断
          }
      }
      
      int main() {
          thread th1(Write);
          thread th2(Write);
          th1.join();
          th2.join();
          cout<<"两个线程执行完毕,最终g_num="<<g_num<<endl;
          return 0;
      }
      #include<iostream>
      #include<thread>
      #include<atomic>
      using namespace std;
      
      atomic<bool> g_flag = false;//线程退出标记
      
      void myThread() {
          cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl;
          while (!g_flag) {
              cout << "myThread() run," << "threadid=" << this_thread::get_id() << endl;
              this_thread::sleep_for(chrono::milliseconds(1000));
          }
          cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl;
      }
      
      int main() {
          thread th1(myThread);
          thread th2(myThread);
          this_thread::sleep_for(chrono::milliseconds(2000));
          g_flag = true;
          th1.join();
          th2.join();
          return 0;
      }

      (6)atomic原子操作,针对++,--,+=,-=,*=,&=,!=是支持的,其它可能不支持

      #include<iostream>
      #include<thread>
      #include<atomic>
      using namespace std;
      
      atomic<int> g_num = 0;
      
      void Write() {
          for (int i = 0; i < 1000000; ++i) {
              g_num = g_num + 1;
          }
      }
      
      int main() {
          thread th1(Write);
          thread th2(Write);
          th1.join();
          th2.join();
          cout << "两个线程执行完毕,最终g_num=" << g_num << endl;
          return 0;
      }

      在这里插入图片描述

      (7)load()函数:以原子方式读取atomic对象的值

      atomic<int> atm1;
      atomic<int> atm2=atm1;//尝试引用已删除的函数,拷贝赋值运算符也不让用
      atomic<int> atm3(atm1.load())

      (8)store()函数:以原子方式写入内容

      atomic<int> atm;
      atm.store(1);

    windows临界区、其他各种mutex互斥量

    • windows临界区

      #include <iostream>
      #include<thread>
      #include<mutex>
      #include<Windows.h>
      using namespace std;
      
      #define WINDOWS
      
      #ifdef WINDOWS
          CRITICAL_SECTION winsec;//windows中的临界区,类似于mutex
      #endif
      
      mutex g_mutex;
      int g_buf[100];//缓冲区,最多存放一百个数
      int g_count = 0;
      
      //第一个线程:生产者
      void Producer() {
          while (true) {
              int r = rand() % 20 + 1;//生成一个1……20之间的随机数
              this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间
              //存放一个物品(这里存放的数据代表物品)
      #ifdef WINDOWS
              EnterCriticalSection(&winsec);//进入临界区,相当于mutex.lock()
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
              LeaveCriticalSection(&winsec);//退出临界区,相当于mutex.unlock()
      #else
              g_mutex.lock();
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
              g_mutex.unlock();
      #endif // WINDOWS
          }
      }
      
      //第二个线程:消费者
      void Consume() {
          while (true) {
              this_thread::sleep_for(chrono::milliseconds(50));//轮询
      #ifdef WINDOWS
              EnterCriticalSection(&winsec);
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
              LeaveCriticalSection(&winsec);
      #else
              g_mutex.lock();
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
              g_mutex.unlock();
      #endif // WINDOWS
      
          }
      }
      
      int main()
      {
      #ifdef WINDOWS
          InitializeCriticalSection(&winsec);//使用临界区之前初始化
      #endif // WINDOWS
      
          srand(time(nullptr));
          thread producer(Producer);
          thread consume(Consume);
          producer.join();
          consume.join();
          return 0;
      }
    • 多次进入临界区试验

      (1)在同一个线程中,windows中相同临界区变量代表的临界区的进入(EnterCriticalSection)可以被多次调用,但是EnterCriticalSection的调用次数需要和LeaveCriticalSection的调用次数要相等(否则,该线程始终都在临界区中,该线程一直执行,另一个线程阻塞)

      (2)c++11中mutex不允许lock多次

    • 自动析构技术

      #include <iostream>
      #include<thread>
      #include<mutex>
      #include<Windows.h>
      using namespace std;
      
      #define WINDOWS
      
      //用于自动释放windows下的临界区,防止忘记LeaveCriticalSection
      //类似与std::lock_guard
      //RAII类(Resource Acquisition initialization):资源获取即初始化
      class cWinLock {
      public:
          cWinLock(CRITICAL_SECTION* ptr) :ptr(ptr) {
              EnterCriticalSection(ptr);
          }
          ~cWinLock() {
              LeaveCriticalSection(ptr);
          }
      private:
          CRITICAL_SECTION* ptr;
      };
      
      #ifdef WINDOWS
          CRITICAL_SECTION winsec;//windows中的临界区,类似于mutex
      #endif
      
      mutex g_mutex;
      int g_buf[100];//缓冲区,最多存放一百个数
      int g_count = 0;
      
      //第一个线程:生产者
      void Producer() {
          while (true) {
              int r = rand() % 20 + 1;//生成一个1……20之间的随机数
              this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间
              //存放一个物品(这里存放的数据代表物品)
      #ifdef WINDOWS
              cWinLock wLock(&winsec);
              cWinLock wLock_(&winsec);
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
      #else
              lock_guard<mutex> lock(g_mutex);
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
      #endif // WINDOWS
          }
      }
      
      //第二个线程:消费者
      void Consume() {
          while (true) {
              this_thread::sleep_for(chrono::milliseconds(50));//轮询
      #ifdef WINDOWS
              cWinLock wLock(&winsec);
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
      #else
              lock_guard<mutex> lock(g_mutex);
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
      #endif // WINDOWS
      
          }
      }
      
      int main()
      {
      #ifdef WINDOWS
          InitializeCriticalSection(&winsec);//使用临界区之前初始化
      #endif // WINDOWS
      
          srand(time(nullptr));
          thread producer(Producer);
          thread consume(Consume);
          producer.join();
          consume.join();
          return 0;
      }
    • recursive_mutex递归的独占互斥量

      std::mutex:独占互斥量,在本线程lock后,本线程无法继续lock【除非unlock后】,其它线程也无法lock

      std::recursive_mutex:递归的独占互斥量,允许同一个线程中同一个互斥量多次被lock【第一次lock后没有unlock】,效率上比mutex低

      #include <iostream>
      #include<thread>
      #include<mutex>
      #include<Windows.h>
      using namespace std;
      
      recursive_mutex g_mutex;
      int g_buf[100];//缓冲区,最多存放一百个数
      int g_count = 0;
      
      //第一个线程:生产者
      void Producer() {
          while (true) {
              int r = rand() % 20 + 1;//生成一个1……20之间的随机数
              this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间
              //存放一个物品(这里存放的数据代表物品)
              lock_guard<recursive_mutex> lock(g_mutex);
              lock_guard<recursive_mutex> lock_(g_mutex);
              g_buf[g_count] = r;
              ++g_count;
              cout << "放入物品:" << r << endl;
          }
      }
      
      //第二个线程:消费者
      void Consume() {
          while (true) {
              this_thread::sleep_for(chrono::milliseconds(50));//轮询
              lock_guard<recursive_mutex> lock(g_mutex);
              if (g_count > 0) {
                  for (int i = 0; i < g_count; ++i) {
                      cout << "消耗物品:" << g_buf[i] << endl;
                  }
                  g_count = 0;
              }
          }
      }
      
      int main()
      {
          srand(time(nullptr));
          thread producer(Producer);
          thread consume(Consume);
          producer.join();
          consume.join();
          return 0;
      }
      
    • 带超时的互斥量std::timed_mutex和std::recursive_timed_mutex

      1. std::timed_mutex:带超时功能的独占互斥量

        (1)try_lock_for():参数是一段时间,等待一段时间,如果等待超时或者lock成功,继续流程

        #include <iostream>
        #include<thread>
        #include<mutex>
        #include<Windows.h>
        using namespace std;
        
        timed_mutex g_mutex;//带超时功能的独占互斥量
        int g_buf[100];//缓冲区,最多存放一百个数
        int g_count = 0;
        
        //第一个线程:生产者
        void Producer() {
            while (true) {
                int r = rand() % 20 + 1;//生成一个1……20之间的随机数
                //存放一个物品(这里存放的数据代表物品)
                if (g_mutex.try_lock_for(10ms)) {//等待100毫秒尝试lock
                    //在100毫秒内lock成功
                    g_buf[g_count] = r;
                    ++g_count;
                    cout << "放入物品:" << r << endl;
                    g_mutex.unlock();
                }
                else {
                    //没有在100毫秒内lock成功
                    cout << "Producer lock失败" << endl;
                    this_thread::sleep_for(chrono::microseconds(100));
                }
            }
        }
        
        //第二个线程:消费者
        void Consume() {
            while (true) {
                this_thread::sleep_for(chrono::milliseconds(50));//轮询
                lock_guard<timed_mutex> lock(g_mutex);
                if (g_count > 0) {
                    for (int i = 0; i < g_count; ++i) {
                        cout << "消耗物品:" << g_buf[i] << endl;
                    }
                    g_count = 0;
                }
            }
        }
        
        int main()
        {
            srand(time(nullptr));
            thread producer(Producer);
            thread consume(Consume);
            producer.join();
            consume.join();
            return 0;
        }

        在这里插入图片描述

     (2)try_lock_until:参数是一个未来时间点,在这个未来时间没到的时间内,不管lock是否成功,流程继续
    
     ```c++
     #include <iostream>
     #include<thread>
     #include<mutex>
     #include<Windows.h>
     using namespace std;
     
     timed_mutex g_mutex;//带超时功能的独占互斥量
     int g_buf[100];//缓冲区,最多存放一百个数
     int g_count = 0;
     
     //第一个线程:生产者
     void Producer() {
         while (true) {
             int r = rand() % 20 + 1;//生成一个1……20之间的随机数
             //存放一个物品(这里存放的数据代表物品)
             if (g_mutex.try_lock_until(chrono::steady_clock::now() + 100ms)) {//当前时间加上100ms
                 //在以当前时刻算起的100ms内,lock成功
                 g_buf[g_count] = r;
                 ++g_count;
                 cout << "放入物品:" << r << endl;
                 g_mutex.unlock();
             }
             else {
                 cout << "Producer lock失败" << endl;
                 this_thread::sleep_for(chrono::microseconds(100));
             }
         }
     }
     
     //第二个线程:消费者
     void Consume() {
         while (true) {
             this_thread::sleep_for(chrono::milliseconds(50));//轮询
             lock_guard<timed_mutex> lock(g_mutex);
             if (g_count > 0) {
                 for (int i = 0; i < g_count; ++i) {
                     cout << "消耗物品:" << g_buf[i] << endl;
                 }
                 g_count = 0;
             }
         }
     }
     
     int main()
     {
         srand(time(nullptr));
         thread producer(Producer);
         thread consume(Consume);
         producer.join();
         consume.join();
         return 0;
     }
     
     ```
    
     
    
    1. std::recursive_timed:带超时功能的递归独占互斥量
    阅读 640
    4 声望
    0 粉丝
    0 条评论
    你知道吗?

    4 声望
    0 粉丝
    宣传栏