• c++多线程在异常环境下的等待


          c++11开始支持多线程编程,相关的类和函数封装在标准库头文件<thread>中,而c++多线程编程很重要的一点就是当用户创建一个std::thread对象,关联了可调用对象后,需要在该thread对象销毁前调用join()或detach()。其中join()函数用于结合线程,确保在创建该thread对象的函数退出前,该线程执行完毕;而detach()函数则表示分离线程,即让线程在后台运行,线程的所有权和控制权交给c++运行时库。若thread对象销毁前没有调用join()或detach(),则程序会被终止(thread的析构函数调用std::terminate() )。

          如果用户要分离线程,那么一般情况下在线程启动后立即调用detach()即可。但若需要等待线程,那么调用join()的时机就很重要:如果线程在开始之后、调用join()之前发生了异常,则join()的调用就会被跳过。保证线程在异常环境下的等待的方法之一是使用try/catch语句块:

     1 #include <thread>
     2 #include <stdexcept>
     3 
     4 using std::thread;
     5 using std::runtime_error;
     6 
     7 void func();
     8 
     9 void TestThread() {
    10     thread thrd(func);
    11     try
    12     {
    13         //...
    14     }
    15     catch (runtime_error re)
    16     {
    17         thrd.join();
    18         throw re;
    19     }
    20     thrd.join();
    21 }

          但是很显然,使用try/catch语句块很罗嗦,且容易将作用域弄乱。一个更好的方法是使用资源获取即初始化(Resource Acquisition Is Initialization, RAII)的方法,RAII是C++的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。

          使用RAII的代码如下:

     1 #include <thread>
     2 
     3 using std::thread;
     4 
     5 void func();
     6 
     7 class Thread_RAII {
     8 private:
     9     thread &thrd;
    10 public:
    11     explicit Thread_RAII(thread &p_thrd) : thrd(p_thrd) {}
    12     ~Thread_RAII()
    13     {
    14         if (thrd.joinable())
    15         {
    16             thrd.join();
    17         }
    18     }
    19     Thread_RAII(const Thread_RAII &) = delete;
    20     Thread_RAII & operator=(const Thread_RAII &) = delete;
    21 };
    22 
    23 void TestThread()
    24 {
    25     thread thrd(func);
    26     Thread_RAII tr(thrd);
    27     //...
    28 }

          当函数TestThread()执行到末尾时,局部对象按照构造的逆序进行销毁,因此tr首先被销毁,于是其中的thrd对象被结合,即使在之前其中的某条语句执行时引发异常而退出时也是如此。

          上述代码还有两点需要注意:

          ①析构函数在调用join()之前需要检查thrd是否是joinable()的,因为对于一个线程,join()只能调用一次。

          ②Thread_RAII类的拷贝构造函数及拷贝复制运算符是删除的,这是因为thread对象和unique_ptr一样是不可拷贝的(但可以移动)。

  • 相关阅读:
    linux中压缩、解压缩命令
    linux中的sed指令
    linux中shell编程(一)
    linux中的正则表达式
    linux中的管道和重定向
    linux中用户、组和权限相关指令
    linux中bash常见的指令
    linux文本操作相关指令
    java.lang.OutOfMemoryError 解决程序启动内存溢出问题
    Java常用排序算法/程序员必须掌握的8大排序算法
  • 原文地址:https://www.cnblogs.com/jzincnblogs/p/5179749.html
Copyright © 2020-2023  润新知