C++11多线程并发中的std::thread、std::mutex和std::future

C++11 新标准中引入了五个头文件来支持多线程编程:<atomic>,<thread>,<mutex>,<condition_variable> 和 <future>

<atomic>: 该文件主要申明了俩个类,std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类和与C兼容的原子操作的函数。
<thread>: 该文件主要声明了 std::thread 类,另外std::thread 命名空间也在该头文件中。
<mutex>: 该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock,以及其他的类型和函数。
<condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
<future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。

一. 类std::thread

1.默认构造函数:thread() noexcept; 构造一个任何线程都不执行的线程对象,即空的thread执行对象。
2.初始化函数:

template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);

构造一个线程对象,可以开启线程执行,该thread对象可被joinable,新执行的线程调用函数fn,并传递args作为参数:
fn:可以指向函数,指向成员,或是移动构造函数
args:传递给fn的参数,这些参数可以移动赋值构造。如果fn是一个成员指针,那么第一个args参数就必须是一个对象,或是引用,或是指向该对象的指针。

3.拷贝构造函数 (被禁用):thread (const thread&) = delete,意味着thread不可被拷贝构造。
4.move构造函数 (移动构造函数):thread (thread && x)noexcept 调用成功后x不代表任何thread执行对象。

std::thread::join
该函数返回时,线程执行完成。
当 一个 thread 调用Join方法的时候,MainThread 就被停止执行,直到该 thread 线程执行完毕。

注意:可被joinable的thread对象必须在他们销毁之前被主线程join或者将其设置为detached。

#include <iostream>    
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds

void pause_thread(int n) 
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}

int main() 
{
  std::cout << "Spawning 3 threads...\n";
  std::thread t1 (pause_thread,1); 
  std::thread t2 (pause_thread,2);
  std::thread t3 (pause_thread,3);
  std::cout << "Done spawning threads. Now waiting for them to join:\n";
  t1.join();  //停下主线程,进入t1 线程
  t2.join();  //进入t2线程
  t3.join();  // 进入t3线程
  std::cout << "All threads joined!\n";

  return 0;
}

std::thread::detach
分离线程的对象,使他们彼此独立地执行所表示的线程。这俩个线程既没有被阻止,也没有以任何方式同步。当任一结束执行,其资源被释放。

#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds

void pause_thread(int n) 
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}

int main() 
{
  std::cout << "Spawning and detaching 3 threads...\n";
  std::thread (pause_thread,1).detach();
  std::thread (pause_thread,2).detach();
  std::thread (pause_thread,3).detach();
  std::cout << "Done spawning threads.\n";

  std::cout << "(the main thread will now pause for 5 seconds)\n";
  // give the detached threads time to finish (but not guaranteed!):
  pause_thread(5);
  return 0;
}
//输出
Output (after 5 seconds):
Spawning and detaching 3 threads...
Done spawning threads.
(the main thread will now pause for 5 seconds)
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 5 seconds ended

更多的thread中的join()和detach()例子。
是否一定要加join()或者detach()呢?其实是不一定的! 看下面程序:

#include<thread>
#include<iostream>
#include <unistd.h>
using namespace std;
class Test{
   
public:
  void run(int num);
};
void Test::run(int num){
   
      int count=100;
      while(count>0){
   
            std::cout<<"aaaa: "<<num<<std::endl;
            count--;
        }
 }
class Test2{
   
 private:
    Test* testClass;
 public:
    int printNum(int inputNum){
   
       cout <<"Test2:"<<inputNum<<endl;
    }
    void setTestClass(Test* testClassInput){
   
     testClass = testClassInput;
  }
};
class System{
   
 private:
    Test2* pTest2;
    Test* pTest;
 public:
    System(){
   
      //在主线程运行
       pTest2 = new Test2();
       //开劈新的线程
       pTest = new Test();
       std::thread* ptTest = new thread(&Test::run, pTest,1);
       pTest2->setTestClass(pTest);
  }
  void printNumViaTest2(int num){
   
       pTest2->printNum(num);
  }
};
int main (){
   
  System test; 
  for(int i=0; i<1000; i++){
   
      test.printNumViaTest2(i);
  }
  //usleep(1000);
  return 0;
}

在上述程序中,没有调用join() 或者 detach() 函数,但是该程序可以正常运行,主要是因为线程在运行的时候,主进程还未退出。有时候,若不用join() 或者 detach() 可能会出现terminate called without an active exception Aborted的错误,这是因为线程还在运行的时候,主进程就退出了。

初始化 thread 类的构造函数
对于类的成员函数,我们需要给出类对象的地址:

#include <iostream>
#include <thread>

using namespace std;
class A
{
public:
  void fun(int a,int b)
  {
    std::cout<<"this is a thread"<<std::endl;
  }
};

int main()
{
  int k=0;
  A a;
  std::thread t(&A::fun,a,k,k+1);
  t.detach();
  return 0;
}

std::thread(&A::fun,a,k,k+1); 这个地方就可以看出thread 类的构造对于成员函数的重载了,std::thread t(函数(成员函数)地址,对象地址,成员函数的参数1,参数2,参数3...)。
相比非成员函数,成员函数需要给出类实例化对象的地址,如果该线程是在同一类的某一成员函数当中被构造,则直接用this 关键字代替即可。

二. std::mutex

1.Mutex系列类:

std::mutex,最基本的Mutex类
std::recursive_mutex,递归Mutex类
std::time_mutex,定时Mutex类
std::recursive_timed_mutex,定时递归Mutex类

2.Lock类

std::lock_guard,与Mutex RAII相关,方便线程对互斥量上锁。
std::unique_lock,与Mutex RAII相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

3.函数

std::try_lock,尝试同时对多个互斥量上锁。
std::lock,可以同时对多个互斥量上锁。
std::call_one,如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次。

std::mutex 介绍

std::mutex 是C++11中最基本的互斥量,std::mutex对象提供了独占所有权的特性—即不支持递归地对std::mutex对象上锁,而std::recursize_lock则可以递归地对互斥量对象上锁。

std::mutex 成员函数

1.构造函数,std::mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象是unlocked状态的。
2.lock(), 调用线程将锁住该互斥量。线程调用该函数会发生下面3种情况: (1) 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。(2) 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞。(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁,故lock()后一般需要 unlock()。
3.unlock(), 解锁,释放对互斥量所有权。
4.try_lock(), 尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被堵塞。出现3种情况:(1) 如果当前互斥量没有被其他锁住,则锁住该互斥量。 (2) 如果当前互斥量被其他线程锁住,则当前调用线程返回false,不会堵塞。(3)如果被当前线程锁住了但没有释放,产生死锁。

加锁和解锁问题

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值