C++11 线程库的使用

本文介绍C++中thread库的基本使用方法,包括线程的创建、管理和资源回收等,并详细阐述了mutex、recursive_mutex、timed_mutex等互斥量的特性与应用,以及条件变量condition_variable的使用案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. thread库
    1. thread只是类库,thread对象可以与实际运行的线程产生联系,利用Thread对象对线程进行操作,但是实际线程的存亡与thread对象的存亡并无绝对的决定关联
    2. 初始化构造函数:传入一个函数和它对应参数
    3. move构造函数:thread(thread&& x) noexcept,x所代表的线程的任务被交给thread对象
    4. join(): 操作是在thread t(func)后“某个地方”调用,其作用就是回收对应创建的线程的资源,避免造成资源的泄露
    5. detach():操作是在std::thread t(func)后马上调用,用于把被创建的线程与做创建动作的线程分离,分离的线程变为后台线程,其后,创建的线程的“死活”就与其做创建动作的线程无关,它的资源会被init进程回收。
    6. get_id():获取当前进程ID
    7. 下面是是两个进程数数的实例程序
    8. #include <iostream>
      #include <utility>
      #include <thread>
      #include <chrono>
      #include <functional>
      #include <atomic>
      
      using namespace std;
      
      void f1(int& n)
      {
      	int cnt = 10000;
      	while (cnt--)
      	{
      		int a = n;
      		int b = a + 1;
      		n = b;
      	}
      }
      
      void f2(int& n)
      {
      	int cnt = 10000;
      	while (cnt--)
      	{
      		int a = n;
      		int b = a + 1;
      		n = b;
      	}
      }
      
      int main()
      {
      	int n = 0;
      	thread t1(f1, ref(n));
      	thread t2(f2, ref(n));
      	t1.join();
      	t2.join();
      	cout << "n === " << n << endl;
      	return 0;
      }
    9.                  
  2. mutex系列类
    1. mutex代表互斥量,与C++11与mutex相关的类和函数都声明在<mutex>头文件
      1. std::mutex,最基本的mutex类
      2.  std::time_mutex,定时 Mutex 类
      3. std::recursive_mutex,递归 Mutex 类。
      4. std::recursive_timed_mutex,定时递归 Mutex 类
    2. 函数
      1. try_lock,尝试对多个互斥量上锁
      2. lock,,同时对多个互斥量上锁
      3. std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次
    3. mutex
      1. std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性。
      2. 构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
      3. lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
      4. unlock(), 解锁,释放对互斥量的所有权。
      5. try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
      6. 下面还是两个线程数数的程序
      7. #include <thread>
        #include <iostream>
        #include <mutex>
        
        using namespace std;
        
        mutex mtx;
        
        void f1(int& n)
        {
            int cnt = 1000000;
            while (cnt--)
            {
                if (mtx.try_lock())
                {
                    int a = n;
                    int b = a + 1;
                    int c = b - 1;
                    int d = c + 1;
                    n = d;
                    mtx.unlock();
                }
                else
                {
                    cnt++;
                }
        
            }
        }
        
        void f2(int& n)
        {
            int cnt = 1000000;
            while (cnt--)
            {
                if (mtx.try_lock())
                {
                    int a = n;
                    int b = a + 1;
                    int c = b - 1;
                    int d = c + 1;
                    n = d;
                    mtx.unlock();
                }
                else
                {
                    cnt++;
                }
            }
        }
        
        int main()
        {
            int n = 0;
            thread t1(f1, ref(n));
            thread t2(f2, ref(n));
            t1.join();
            t2.join();
            cout << "n == " << n << endl;
            return 0;
        }
        

         

    4.  recursive_mutex
      1. std::recursive_mutex 与 std::mutex 一样,也是一种可以被上锁的对象,但是和 std::mutex 不同的是,std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同。
    5. time_mutex
      1. std::time_mutex 比 std::mutex 多了两个成员函数,try_lock_for(),try_lock_until()。

        try_lock_for 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁并返回true,如果超时(即在指定时间内还是没有获得锁),则返回 false。

        try_lock_until 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

      2. 没有获得锁的线程每隔200ms打印一个‘-’,程序如下所示

      3. #include <iostream>       // std::cout
        #include <chrono>         // std::chrono::milliseconds
        #include <thread>         // std::thread
        #include <mutex>          // std::timed_mutex
        
        using namespace std;
        
        timed_mutex mtx;
        
        void f(int n)
        {
            while (!mtx.try_lock_for(chrono::milliseconds(200)))
            {
                cout << "-" << n;
            }
            this_thread::sleep_for(chrono::milliseconds(1000));
            // 当一个线程获得锁之后就不会出现在上面的横杠里
            cout << "*" << n << endl;;
            mtx.unlock();
        }
        
        int main()
        {
            thread threads[10];
            for (int i = 0; i < 10; i++)
            {
                threads[i] = thread(f, i);
            }
            for (int i = 0; i < 10; i++)
            {
                threads[i].join();
            }
            return 0;
        }
        

  3. condition_variable(条件变量)
    1.  condition_variable实际上是一个类,是一个和条件相关的一个类,等待一个条件达成,这个类需要和互斥量来配合工作,用的时候我们要生成这个类的对象。
    2. wait()函数用于等待一个消息,他的第二个参数lambda表达式返回值是true,那wait()直接返回,否则wait()将解锁互斥量,并堵塞到本行,堵塞到其他某个线程调用notify_one()成员函数为止。如果wait()没有第二个参数,那么结果和lambda表达式返回false一样。
    3. 当线程调用notify_one()唤醒wait()(原先堵塞状态)时,wait()恢复后,不断地尝试重新获取互斥量锁,如果获取不到,那么流程卡在wait这里等着获取,如果获取到了锁,那么就判断wait()是都有第二个参数,如果有,那么判断lambda表达式值的真假,如果为true,则wait返回,流程走下去(此时互斥锁被锁着),如果为false那么wait对互斥量解锁,继续睡眠堵塞状态。如果没有结果和true的结果是一致的。
    4. 下面就是利用互斥量和条件变量实现的一个多线程队列
    5. #include <iostream>
      #include <list>
      #include <mutex>
      #include <thread>
      #include <condition_variable>
      
      using namespace std;
      
      class MessageQueue
      {
      public:
          void Push()
          {
              for (int i = 0; i < 10000; i++)
              {
                  cout << "Push " << i <<endl;
                  unique_lock<mutex> lk(mtx);
                  que.push_back(i);
                  my_cond.notify_one();
              }
          }
      
          void Pop()
          {
              while (true)
              {
                  unique_lock<mutex> lk(mtx);
                  my_cond.wait(lk, [this]()->bool {return !this->que.empty();});
                  int item = que.front();
                  que.pop_front();
                  cout << "Pop " << item << endl;
              }
      
          }
      private:
          mutex mtx;
          list<int> que;
          condition_variable my_cond;
      };
      
      int main()
      {
          MessageQueue a;
          thread in(&MessageQueue::Push, &a);
          thread out(&MessageQueue::Pop, &a);
          in.join();
          out.join();
          return 0;
      }
      

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值