• c++11并发语法初步


    c++11并发语法初步

    参考:

    https://www.cnblogs.com/haippy/p/3284540.html

    https://en.cppreference.com/w/

    https://wizardforcel.gitbooks.io/cpp-11-faq/content/77.html

    http://www.cplusplus.com/reference/

    C++11多线程头文件

    C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是 ,,,<condition_variable>和

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

    多线程的Hello World

    #include <bits/stdc++.h>
    #include <thread>
    
    using namespace std;
    
    void function_name() {
        cout << "hello world" << endl;
    }
    
    int main()
    {
        std::thread t(function_name);
        t.join();
    
        return 0;
    }
    

    std::thread 构造

    • (1). 默认构造函数,创建一个空的 thread 执行对象。
    • (2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
    • (3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
    • (4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。

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

    其他函数

    get_id 获取线程ID
    joinable 检查线程是否可以join
    join join线程
    detach detach线程
    swap swap线程
    native_handle 返回native_handle
    hardware_concurrency[static] 检查硬件并发性

    参考:https://en.cppreference.com/w/cpp/thread/thread/thread

    #include <iostream>
    #include <utility>
    #include <thread>
    #include <chrono>
     
    void f1(int n)
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 1 executing
    ";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
     
    void f2(int& n)
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 2 executing
    ";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
     
    class foo
    {
    public:
        void bar()
        {
            for (int i = 0; i < 5; ++i) {
                std::cout << "Thread 3 executing
    ";
                ++n;
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
        int n = 0;
    };
     
    int main()
    {
        int n = 0;
        foo f;
        std::thread t1; // t1 is not a thread
        std::thread t2(f1, n + 1); // pass by value
        std::thread t3(f2, std::ref(n)); // pass by reference
        std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
        std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
        t2.join();
        t4.join();
        t5.join();
        std::cout << "Final value of n is " << n << '
    ';
        std::cout << "Final value of foo::n is " << f.n << '
    ';
    }
    
    Thread 1 executing
    Thread 2 executing
    Thread 3 executing
    Thread 3 executing
    Thread 1 executing
    Thread 2 executing
    Thread 2 executing
    Thread 3 executing
    Thread 1 executing
    Thread 3 executing
    Thread 2 executing
    Thread 1 executing
    Thread 3 executing
    Thread 1 executing
    Thread 2 executing
    Final value of n is 5
    Final value of foo::n is 5
    
    std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
    

    保留疑惑,函数名即是地址,加&还是地址,但是看别人怎么写。

    move (1) thread& operator= (thread&& rhs) noexcept;
    copy [deleted] (2) thread& operator= (const thread&) = delete;
    • (1). move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
    • (2). 拷贝赋值操作被禁用,thread 对象不可被拷贝。

    互斥量

    Mutex 系列类(四种)

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

    Lock 类(两种)

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

    其他类型

    • std::once_flag
    • std::adopt_lock_t
    • std::defer_lock_t
    • std::try_to_lock_t

    函数

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

    并发最大的问题就是对临界区资源的访问,主要是通过互斥量上锁的方法解决。

    http://www.cplusplus.com/reference/mutex/mutex/try_lock/ 具体细节看库即可,下面解析一些常见的

    主要用unique_lock,在构造函数上锁,在析构函数解锁,第一个参数为互斥量,第二个是可选

    • std::adopt_lock_t
    • std::defer_lock_t
    • std::try_to_lock_t

    https://en.cppreference.com/w/cpp/thread/lock_tag

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int main()
    {
    
        std::mutex my_mutex;
        std::unique_lock<std::mutex> lock(my_mutex);
    
        ///表示这个互斥量已经lock成功了,通知构造函数不用在lock了,
        my_mutex.lock();    ///必须先lock才能用
        std::unique_lock<std::mutex> lock1(my_mutex, std::adopt_lock);
    
        ///后面会自己unlock()
    
    
        ///============================
        ///尝试锁,没锁成功不阻塞, 前提互斥量不能被锁定
        std::unique_lock<std::mutex> lock2(my_mutex, std::try_to_lock);
        if(lock.owns_lock()) {
            ///拿到了锁头
    
        } else {
            ///没有拿到锁头
        }
    
        ///============================
        ///我没加锁,后面手动锁,方便调用函数, 前提互斥量不能被锁定
        std::unique_lock<std::mutex> lock3(my_mutex, std::defer_lock);
        lock3.lock();   ///加锁
    
        lock3.unlock(); ///不加锁
    
        if(lock3.try_lock() == true) {
            ///拿到锁头了
        } else {
            ///没拿到
    
        }
    
        ///释放互斥量
        std::mutex *m = lock3.release();
    
        return 0;
    }
    

    std::condition_variable

    一个线程等待另一个线程条件满足

    void function_name() {
        unique_lock<mutex> lock(mutex1);
    
        ///能往下走,肯定互斥量默认被锁了,到wait。
        ///第二个参数可选,没写默认false。true为不堵塞,继续走。
        ///如果是false卡在这等待其他线程的con.notify_one()的函数
        ///注意notify_one的时候必须卡在wait的地方,否则唤醒失败
    	///notify_all()
    	
        con.wait(lock, [] () {
            if(true) {
                return true;
            }
            return false;
        });
    }
    

    std::async, std::future 线程返回值

    std::async启动一个异步任务后,返回一个std::future访问异步操作结果的机制

    #include <bits/stdc++.h>
    #include <future>
    
    using namespace std;
    
    int mythread() {
        cout << "mythread = " << this_thread::get_id() << endl;
        return 5;
    }
    
    int main () {
    
        cout << "main = " << this_thread::get_id() << endl;
        ///压根没创建子线程,在主线程中做,视乎默认是这个
        std::future<int>ret = std::async(std::launch::deferred, mythread);
      ///async 新线程在这里开始
        std::future<int>ret = std::async(std::launch::async, mythread);
        cout << ret.get();	///卡在这里等结果
        return 0;
    }
    
    

    用packaged_task打包线程,就多了一个可以返回结果的future接口

    #include <bits/stdc++.h>
    #include <future>
    #include <thread>
    using namespace std;
    int mythread() {
        cout << "mythread = " << this_thread::get_id() << endl;
        return 5;
    }
    int main () {
        ///std::packaged_task 打包可调用对象
        std::packaged_task<int()>mypt(mythread);
        std::thread t1(std::ref(mypt));
        t1.join();
    
        std::future<int>res = mypt.get_future();
        return 0;
    }
    

    后续就可以这么用

        ///
        vector<std::std::packaged_task<int()>> vec;
        ///不用移动语义可能会出问题
        vec.push_back(std::move(mypt));
    
  • 相关阅读:
    数组相关操作
    控制结构和函数
    调用函数和方法
    常用类型、算术和操作符重载
    函数式 CSS (FCSS)
    Javascript 封装问题
    认识javascript中的作用域和上下文
    Javascript 继承-原型的陷阱
    解耦你的HTML,CSS和JAVASRIPT
    网格如此简单
  • 原文地址:https://www.cnblogs.com/Q1143316492/p/10396322.html
Copyright © 2020-2023  润新知