• C++11并发编程——多线程


    引言

    C++11 引入了 thread 类,降低了使用多线程的复杂度,原先使用多线程只能用系统的 API,无法解决跨平台问题,代码平台的改变,对应多线程代码也必须要修改。
    在 C++11 中只需使用语言层面的 thread 可以解决这个问题。编写并发程序需引入头文件。

    1线程库

     管理当前线程的函数,定义于thread
    
    • 1

    1.2构造函数和赋值

    构造函数功能
    thread() ;①默认构造函数,构造不表示线程的新 thread 对象
    thread( thread&& other );②移动构造函数。构造表示曾为 other 所表示的执行线程的 thread 对象。此调用后 other 不再表示执行线程。
    template< class Function, class… Args >explicit thread( Function&& f, Args&&… args );③初始化构造函数;构造新的 std::thread 对象并将它与执行线程关联。

    拷贝构造函数被禁用,std::thread 对象不可拷贝构造,也不可以赋值。

    1.2.1初始化构造函数和默认构造函数的使用

    #include 
    #include 
    
    using namespace std;
    
    void threadfun(int a)//值传递
    {
        cout << "threadfun: " << endl;
        cout << a << endl;
    }
    
    int main()
    {
        // ①默认构造函数,构造不表示线程的新 thread 对象(很少使用)
        thread th1();
    
        //③构造新的 std::thread 对象并将它与执行线程关联。
        thread th3(threadfun, 3);//创建 std::thread 执行对象,线程调用 threadFun 函数,函数参数为 args。    
        
        cout << "main end" << endl;
            
        th3.join();//使th3线程执行完毕(可尝试挪动这条语句位置,观察结果)
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    1.2.2移动构造函数的使用

    #include 
    #include 
    
    using namespace std;
    
    void threadfun(int a)//值传递
    {
        cout << "threadfun: " << endl;
        cout << a << endl;
    }
    
    int main()
    {   
        //③构造新的 std::thread 对象并将它与执行线程关联。
        thread th3(threadfun, 3);
        
        thread th2(std::move(th3));//移动构造函数(th3线程失去所有权)
    
        cout << "main end" << endl;        
        
        th2.join();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    1.3成员函数

    成员函数功能
    std::thread::id get_id() ;:获取线程 ID,返回类型 std::thread::id 对象。()
    void join();创建线程执行线程函数,调用该函数会阻塞当前线程,直到线程执行完 join 才返回。
    void detach();从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。detach 调用之后,目标线程就成为了守护线程,驻留后台运行,与之关联的 std::thread 对象失去对目标线程的关联,无法再通过 std::thread 对象取得该线程的控制权。
    bool joinable() const ;检查 std::thread 对象是否标识活跃的执行线程。
    void swap( std::thread& other ) ;交换二个 thread 对象的底层柄。
    static unsigned int hardware_concurrency() ;返回实现支持的并发线程数。
    #include 
    #include 
    
    using namespace std;
    
    void threadfun(int &a)//引用
    {
        cout << "threadfun: " << endl;
        cout << "thread id: " << std::this_thread::get_id << endl;
        cout <<"a = "<< (a += 10 )<< endl;    
    }
    
    int main()
    {
        int a = 3;
        int b = 5;
        cout << "支持的并发线程数: " << std::thread::hardware_concurrency() << endl;
        thread th1(threadfun, std::ref(a)); 
        thread th2(threadfun, std::ref(b));
       
        cout << "th1.get_id: " << th1.get_id() << endl;
        cout << "th2.get_id: " << th2.get_id() << endl;
    
        cout << "after starting: " << th1.joinable() <<endl;
        th1.join();
        cout << "after  join: " << th1.joinable() << endl;    
    
        cout << "main end" << endl;       
        th2.join();    
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    1.4拓展:.创建建线程,线程函数为类成员函数

    #include 
    #include 
    
    using namespace std;
    class Object
    {
    private:
        int value;
    public:
        Object(int x = 0) :value(x)
        {
            cout << "Constructor Object: " << this << endl;
        }
        ~Object()
        {
            cout << "Destroy Object: " << this << endl;
        }
        void fun(string info)
        {
            cout << info << value << endl;
        }
    };
    int main()
    {
        Object obj;
        string str = "我是一个类的成员函数!";
        thread t1(&Object::fun, &obj, str);
        t1.join();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    2.管理当前线程的函数(定义于命名空间 this_thread)

    管理当前线程的函数功能
    void yield() ;建议实现重新调度各执行线程,可以将本线程的 CPU 时间片放弃,并允许其他线程运行。
    template< class Rep, class Period >void sleep_for( const std::chrono::duration& sleep_duration );阻塞当前线程执行,至少经过指定的 sleep_duration 。(使当前线程的执行停止指定的时间段)
    template< class Clock, class Duration >void sleep_until( const std::chrono::time_point& sleep_time );阻塞当前线程,直至抵达指定的 sleep_time
    get_id函数已描述;
    yield 方法其实就是::Sleep(0)。
    Sleep会交出CPU时间片,允许其他线程运行,但“其他线程”也包含了交出 CPU 时间片的那个线程。 
    想要更好的进行线程切换,不能够使用 Sleep,而应采用线程锁或其他线程切换方法。
    
    • 1
    • 2
    • 3
    • 4
    #include 
    #include 
    
    using namespace std;
    
    void threadfun(int &a)//引用
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        cout << "threadfun: " << endl;
        cout <<"a = "<< (a += 10 )<< endl;    
    }
    
    int main()
    {
        int a = 3;
        int b = 5;
        
        thread th1(threadfun, std::ref(a)); 
        thread th2(threadfun, std::ref(b)); 
    
        cout << "main " << endl; 
        th1.join();th2.join();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    3.多线程获取返回值方法

    在 C++11 多线程中 std::thread 对象会忽略顶层函数的返回值。
    但是在许多时候,我们想要得到线程返回的值。那么该如何操作?
    
    • 1
    • 2

    3.1 方法1(指针法)

    #include 
    #include 
    
    using namespace std;
    
    //线程返回值 = 形参1 + 形参2
    void threadfun(int a, int b, shared_ptr<int> add)
    {
        *add = a + b;
    }
    
    int main()
    {
        int a = 3;
        int b = 5;
        shared_ptr<int> add(new int(0));
    
        cout << "before add: " << *add << endl;
        thread th1(threadfun, a,b,add); 
    
        th1.join();//等待th1线程运行完毕
    
        cout << "after add: " << *add << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    3.2 方法2(使用std::future(期望)和std::promise(承诺)来取数据)

    std::future和std::promise是封装好的两个类模板,这两个类需要配合使用,它们的头文件是’future’(应该使用<>),
    唯一期望(std::future)的实例只能与一个指定事件相关联。
    共享期望(std::shared_future<>)的实例就能关联多个事件。
    std::future,表示存储着一个未来会被初始化的变量。这个变量可以通过std::future提供的成员函数std::future::get()来得到。如果在这个变量被赋值之前有别的线程试图通过std::future::get()获取这个变量,那么这个线程将会被阻塞到这个变量可以获取为止。
    std::promise同样也是一个类模板,这个对象承诺在未来一定会初始化一个变量(std::future中的变量),每一个std::promise对象都有一个与之关联的std::future对象。当std::promise设置值的时候,这个值就会赋给std::future中的对象了。

    #include
    #include
    #include
    
    void func2(int x, int y, std::promise<int>& promiseObj)
    {
        cout << "in func " << endl;
        
        std::this_thread::sleep_for(std::chrono::seconds(2));
        promiseObj.set_value(x + y);
        
        cout << "out func " << endl;
    }
    
    int main(void)
    {
        int a = 10, b = 20;
    
        int sum = 0;
        std::promise<int> promiseObj;
        
        //将 future 和 promise 关联
        std::future<int> futureObj = promiseObj.get_future();
        
        //模板传参引用类型的时候使用 ref,否则传参失败
        std::thread ta(func2, a, b, std::ref(promiseObj));
        
        //获取值
        sum = futureObj.get();// 等待线程计算出值
        std::cout << "sum =" << sum << std::endl;
    
        ta.join();
    
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

  • 相关阅读:
    创建springboot(三)用户登录
    《C++primer》读书笔记---第九章:顺序容器
    C++中位运算符使用
    java-php-net-python-基于的相册软件的设计与实现计算机毕业设计程序
    大数据必学Java基础(十八):条件运算符和位运算符
    C++11常用新特性——右值引用&&
    Linux下安装开源杀毒软件ClamAV对服务器进行查杀
    架构师的 36 项修炼第11讲:致未来的架构师
    一文理解分布式开发中的服务治理
    【漏洞复现】短视频矩阵营销系统 ajax_uplaod接口处存在任意文件上传
  • 原文地址:https://blog.csdn.net/qq_44423388/article/details/127595740