• c++11 多线程 2<<c++ concurrency in action>>


    本章的讲解是围绕thread的接口进行的,下面是thread的接口定义:

     

    class thread{
    public:
        class id;    //声明
        //construction an destruction
        //**一、构造
        thread()    noexcept;
        ~thread();
        template<typename Callable, typename Args...>
        explicit thread(Callable&& func, Args&&...args);
        //copy and move
        //**二、拷贝、赋值
        thread(const thread& other) = delete;
        thread(thread&& other) noexcept;
    
        thread& operator=(const thread& other) = delete;
        thread& operator=(thread&& other) noexcept;
        //一个好的类一定实现一个异常安全的swap(effective c++)
        void swap(thread& other) noexcept;
        //**三、join、detach
        void join();
        void detach();
        bool joinable() const noexcept;
        //**四、class id
        id get_id() const noexcept;
        native_handle_type native_handle();
        //**五、
        static unsigned hardware_concurrency() noexcept;
    }

     

    一、构造:

    暂时忽略默认构造函数,只剩下一个构造函数了,这是一个可变模板参数的构造函数,并且使用了完美转发。下面针对参数讨论三点

    1Callable函数对象(callable object,标准库这样叫的),有四种:functionmember functionfunction objectlambda,具体使用如下代码所示:

     

     

    #include<thread>
    #include<iostream>
    
    class BackgroundClass{
    public:
        void operator()()const{
            std::cout << "function object" << std::endl;
        }
        void ClassMem()const{ std::cout << "member function" << std::endl; }
    };
    void Func(){ std::cout << "function"<<std::endl; }
    int main(){
        //1
        //std::thread myThread(BackgroundClass()); 这是构建一个函数myThread,返回thread,参数是一个临时对象
        //采用加括号std::thread myThread((BackgroundClass()))
    //或利用统一初始化std::thread myThread{BackgroundClass()};
        std::thread myThread((BackgroundClass()));
        //2
        //std::thread myThread(&BackgroundClass::ClassMem,BackgroundClass());
        //3
        //std::thread myThread([](){std::cout << "lambda" << std::endl; });
        //4
        //std::thread myThread(Func);
        myThread.join();
        return 0;
    }

    2、给callable object 传递参数:

      正如可变参数模板使用一样,只要将需要的参数按顺序写到callable object后面即可。

      需要注意的是,thread会将传递给他的参数原照原拷贝下来(实参是指针就拷贝指针;实参是对象就拷贝对象,即使形参是引用),然后thread在函数调用时候,利用这些拷贝下来的参数去调用。

     

    int main(){
        std::string str(“jia”);
        std::thread myThread([](std::string&str){
            str += ”feng”;
        }, str);
        myThread.join();
        std::cout << str << std::endl;    //print:jia
        return 0;
    }

    lambda形参为引用就是为了改变str,但是thread会把str拷贝到临时对象,然后用这个临时对象去调用这个lambdalambda引用的是这个临时对象,当线程结束时候,临时对象销毁,并没有达到修改str的目的。要修改必须要让thread知道你传递是的”引用”,这可以利用std::ref(str)做到。

    td::thread myThread([](std::string&str){
        str += ”feng”;
    }, std::ref(str));
    //print::jiafeng

    如果使用指针是不会出现这个问题的,因为同一个进程同一地址空间。

    std::thread myThread([](string* pstr){
        (*pstr) += ”feng”;
    }, &str);
    //print:jiafeng

    注意智能指针unique_ptr,他们有拷贝构造只有移动,因此只能用移动构造传参:

    void ProcessBigObject(std::unique_ptr<big_object>);
    
    std::unique_ptr<big_object> p(new big_object);
    
    std::thread t(ProcessBigObject,std::move(p));

    这里首先将big_object moveinternal storage然后再move到函数调用。

     

    3、thread对象创建效果:使用默认构造函数只是创建一个标记,并未有任何实质线程,get_id()返回默认结果,joinable()返回false;利用函数对象创建线程对象,线程会立刻加载执行,如果未能成功加载就会抛出异常。

     

    二、拷贝、赋值:

      threadunique_ptrIO一样,没有拷贝构造、赋值,只有移动构造,移动赋值,这注定了threadunique_ptr相似,里面的真正线程只能在thread对象之间转移,不能出现两个thread对象内部是同一个线程,就像两个unique_ptr不能指向同一个对象一样。

    1、拥有权转移:

     

    void some_func();
    void som_other_func();
    std::thread t1(some_func);         //t1拥有some_func
    std::thread t2(some_other_func);    //t2拥有some_other_func
    t1=std::thread(some_other_func);  //程序崩溃解释如下

     

      临时对象激活t1的移动构造函数,在得到some_other_func之前,t1会先调用自己的析构函数,由于t1没有调用detachjoin(后面详细介绍),因此析构函数会调用std::terminate程序崩溃

     

    std::thread t3;                    //t3什么都没有
    t3=std::thread(some_func);        //激活t3的移动构造,t3此时拥有some_func
    std::thread t4;
    t4=std::move(t3);                //将t3的some_func转交给t4

     

    2thread对象作为函数调用参数、返回对象:

     

    void func1(std::thread);
    func1(std::move(t1));
    func1(std::thread(some_func));
    std::thread g(){
        //1 return std::thread(some_func);
        std::thread t(some_func);
        return t;
    }

     

    三、joindetach:

     

      当一个thread对象构建完后、生命有效期内必须调用joindetach,如果不这样做,thread对象的生命期结束,调用析构函数时会调用std::terminate(),致使应用程序over

     

      当你不需要等待一个线程的执行结果时,在构建完thread对象后直接调用detach即可,往后拖延一方面有异常危险,一方面是资源的浪费。

     

      当你需要等待一个线程的执行结果时,必须使用join。使用join的麻烦在于“将join放在什么位置?”,如果太靠前,主线程阻塞等待辅助线程结束,起不到并行的目的,如果放的太靠后,从thread对象构建到join的调用中间代码很多,很多代码意味着出现异常的可能性很大。join是必须调用的,即使中间代码抛出异常,否则即使你处理了中间代码抛出的异常,应用程序照样over,因为join不调用辅助线程会over整个应用程序。

     

      启发于RAII,使用对象管理资源(effective c++),创建一个对象管理threadjoin

     

    class ScopedThread{
        std::thread m_thread;
    public:
        //线程只能移动,传参要用std::move()或临时对象
        explicit ScopedThread(std::thread t) :m_thread(std::move(t)){
            if (!m_thread.joinable()){
                //传递进来的线程是空的
                throw std::logic_error(“no thread have constructed!”)
            }
        }
        ~ScopedThread(){
            m_thread.join();
        }
        //禁止拷贝、赋值
        ScopedThread(const ScopedThread&) = delete;
        ScopedThread& operator=(const ScopedThread&) = delete;
    }
    //使用:
    int main(){
        //不管后面是否抛出异常,都能保证thread::join调用
        ScopedThread(std::thread(some_func));
        some_func_with_exception();
        return 0;
    }

     

    四、class id

    线程的唯一标识类,定义了各种比较操作符,因此可以用作关联容器中的索引。所有空线程id一样,如果两个id相等要么指向同一个线程,要么指向的两个线程实际都是空的。std::cout<<id<<std::endl;输入结果依赖于平台,因为标准只规定了两个不一样的线程输出结果不一样就行了,没有太多。

    五、hardware_concurrency:

      如果调用成功就返回当前应用程序可以支持的线程数,否则返回0。这个函数不是一定调用成功的,因此调用后要进行判断。

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    百度API车牌识别——Restful方式
    cxgrid 满足条件颜色显示
    cxgrid 增加右键菜单
    折线图堆积图
    echarts 堆积图
    echarts 柱型图和折线图组合
    图表自动轮播
    Delphi D10.3 FMX android 调用自带浏览器打开网页
    echarts-JSON请求数据
    堆叠条形图
  • 原文地址:https://www.cnblogs.com/jiafenggang/p/5483384.html
Copyright © 2020-2023  润新知