• C++11多线程------总结版


      在 Linux 中,有 Pthread; windows 中有CreateThread 与 CloseHandle;但有了 C++11 的 std::thread 以后,你可以在语言层面编写多线程程序了,直接的好处就是多线程程序的可移植性得到了很大的提高,所以作为一名 C++ 程序员,熟悉 C++11 的多线程编程方式还是很有益处的。下面介绍c++11多线程的介绍。

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

    • <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
    • <thread>:该头文件主要声明了 std::thread 类,另外 std::this_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() 函数就声明在此头文件中。

    1、thread

    std::thread 在 <thread> 头文件中声明,因此使用 std::thread 时需要包含 <thread> 头文件。

    相关函数:

    // thread example
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
     
    void foo() 
    {
      // do stuff...
    }
    
    void bar(int x)
    {
      // do stuff...
    }
    
    int main() 
    {
      std::thread first (foo);     // spawn new thread that calls foo()
      std::thread second (bar,0);  // spawn new thread that calls bar(0)
    
      std::cout << "main, foo and bar now execute concurrently...
    ";
    
      // synchronize threads:
      first.join();                // pauses until first finishes
      second.join();               // pauses until second finishes
    
      std::cout << "foo and bar completed.
    ";
    
      return 0;
    }

    2、mutex 互斥锁

    C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在 <mutex> 头文件。

    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 可以保证多个线程对该函数只调用一次。

    std::unique_lock:

    #include <iostream>
    #include<thread>
    #include<unistd.h>
    #include<mutex>
    using namespace std;
    std::mutex mymutex;
    void sayHello()
    {
        int k=0;
        unique_lock<mutex> lock(mymutex);
        while(k<2)
        {
            k++;
            cout<<endl<<"hello"<<endl;
            sleep(2);
        }
    }
    void sayWorld()
    {
        unique_lock<mutex> lock(mymutex);
        while(1)
        {
             cout<<endl<<"world"<<endl;
             sleep(1);
        }
    }
    int main()
    {
       thread threadHello(&sayHello);
       thread threadWorld(&sayWorld);
       threadHello.join();
       threadWorld.join();
       return 0;
    }

    首先同时运行threadHello线程和threadWorld线程
    先进入threadHello线程的sayHello()函数,这个时候加了mymutex锁,另外一个threadWorld线程进入后发现mymutex锁没有释放,只能等待。
    当过去两个循环(每个循环2秒后)threadHello线程结束,unique_lock lock(mymutex)的生命周期结束,mymutex锁释放,执行threadWorld线程,此时开始一直say world。

    std::unique_lock 的构造函数的数目相对来说比 std::lock_guard 多,其中一方面也是因为 std::unique_lock 更加灵活,从而在构造 std::unique_lock 对象时可以接受额外的参数

    3、future

    <future> 头文件中包含了以下几个类和函数:

    • Providers 类:std::promise, std::package_task
    • Futures 类:std::future, shared_future.
    • Providers 函数:std::async()
    • 其他类型:std::future_error, std::future_errc, std::future_status, std::launch.

    (1) std::promise 是C++11并发编程中常用的一个类,常配合std::future使用。其作用是在一个线程t1中保存一个类型typename T的值,可供相绑定的std::future对象在另一线程t2中获取;一个线程获取另一个线程的运算结果。

      可以通过 get_future 来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)

    • promise 对象是异步 Provider,它可以在某一时刻设置共享状态的值。
    • future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。
    #include <iostream>
    #include <future>
    #include <chrono>
     
    void Thread_Fun1(std::promise<int> &p)
    {
        //为了突出效果,可以使线程休眠5s
        std::this_thread::sleep_for(std::chrono::seconds(5));
     
        int iVal = 233;
        std::cout << "传入数据(int):" << iVal << std::endl;
     
        //传入数据iVal
        p.set_value(iVal);
    }
     
    void Thread_Fun2(std::future<int> &f)
    {
        //阻塞函数,直到收到相关联的std::promise对象传入的数据
        auto iVal = f.get();        //iVal = 233
     
        std::cout << "收到数据(int):" << iVal << std::endl;
    }
     
    int main()
    {
        //声明一个std::promise对象pr1,其保存的值类型为int
        std::promise<int> pr1;
        //声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
        std::future<int> fu1 = pr1.get_future();
     
        //创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
        std::thread t1(Thread_Fun1, std::ref(pr1));
        //创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
        std::thread t2(Thread_Fun2, std::ref(fu1));
     
        //阻塞至线程结束
        t1.join();
        t2.join();
     
        return 1;
    }

    (2)std::packaged_task

    std::packaged_task它包装了一个可调用的目标(如function, lambda expression, bind expression, or another function object),以便异步调用,它和promise在某种程度上有点像,promise保存了一个共享状态的值,而packaged_task保存的是一 个函数。  主线程获取子线程中运算结果

    // packaged_task example
    #include <iostream>     // std::cout
    #include <future>       // std::packaged_task, std::future
    #include <chrono>       // std::chrono::seconds
    #include <thread>       // std::thread, std::this_thread::sleep_for
    
    // count down taking a second for each value:
    int countdown (int from, int to) {
      for (int i=from; i!=to; --i) {
        std::cout << i << '
    ';
        std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      std::cout << "Lift off!
    ";
      return from-to;
    }
    
    int main ()
    {
      std::packaged_task<int(int,int)> tsk (countdown);   // set up packaged_task
      std::future<int> ret = tsk.get_future();            // get future
    
      std::thread th (std::move(tsk),10,0);   // spawn thread to count down from 10 to 0
    
      // ...
    
      int value = ret.get();                  // wait for the task to finish and get result
    
      std::cout << "The countdown lasted for " << value << " seconds.
    ";
    
      th.join();
    
      return 0;
    }

    (3)std::future,std::shared_future

      std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)

      std::shared_future 与 std::future 类似,但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。shared_future 可以通过某个 std::future 对象隐式转换(参见 std::shared_future 的构造函数),或者通过 std::future::share() 显示转换,无论哪种转换,被转换的那个 std::future 对象都会变为 not-valid.

    4、atomic

      所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。

      C++中对共享数据的存取在并发条件下可能会引起data race的undifined行为,需要限制并发程序以某种特定的顺序执行,有两种方式:使用mutex保护共享数据,原子操作:针对原子类型操作要不一步完成,要么不做,不可能出现操作一半被切换CPU,这样防止由于多线程指令交叉执行带来的可能错误。非原子操作下,某个线程可能看见的是一个其它线程操作未完成的数据。

    #include <iostream>       // std::cout
    #include <atomic>         // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
    #include <thread>         // std::thread, std::this_thread::yield
    #include <vector>         // std::vector
     
    // 由 false 初始化一个 std::atomic<bool> 类型的原子变量
    std::atomic<bool> ready(false);
    std::atomic_flag winner = ATOMIC_FLAG_INIT;
     
    void do_count1m(int id)
    {
        while (!ready) { std::this_thread::yield(); } // 等待 ready 变为 true.
     
        for (volatile int i=0; i<1000000; ++i) {} // 计数
     
        if (!winner.test_and_set()) {
          std::cout << "thread #" << id << " won!
    ";
        }
    }
     
    int main ()
    {
        std::vector<std::thread> threads;
        std::cout << "spawning 10 threads that count to 1 million...
    ";
        for (int i=1; i<=10; ++i) threads.push_back(std::thread(count1m,i));
        ready = true;
     
        for (auto& th : threads) th.join();
        return 0;
    }
    #include <iostream>             // std::cout
    #include <atomic>               // std::atomic
    #include <thread>               // std::thread, std::this_thread::yield
     
    std::atomic <int> foo = 0;
     
    void set_foo(int x)
    {
        foo = x; // 调用 std::atomic::operator=().
    }
     
    void print_foo()
    {
        while (foo == 0) { // wait while foo == 0
            std::this_thread::yield();
        }
        std::cout << "foo: " << foo << '
    ';
    }
     
    int main()
    {
        std::thread first(print_foo);
        std::thread second(set_foo, 10);
        first.join();
        second.join();
        return 0;
    }
    #include <iostream>       // std::cout
    #include <atomic>         // std::atomic
    #include <thread>         // std::thread
    #include <vector>         // std::vector
     
    // a simple global linked list:
    struct Node { int value; Node* next; };
    std::atomic<Node*> list_head(nullptr);
     
    void append(int val)
    {
        // append an element to the list
        Node* newNode = new Node{val, list_head};
     
        // next is the same as: list_head = newNode, but in a thread-safe way:
        while (!list_head.compare_exchange_weak(newNode->next,newNode)) {}
        // (with newNode->next updated accordingly if some other thread just appended another node)
    }
     
    int main ()
    {
        // spawn 10 threads to fill the linked list:
        std::vector<std::thread> threads;
        for (int i = 0; i < 10; ++i) threads.push_back(std::thread(append, i));
        for (auto& th : threads) th.join();
     
        // print contents:
        for (Node* it = list_head; it!=nullptr; it=it->next)
            std::cout << ' ' << it->value;
     
        std::cout << '
    ';
     
        // cleanup:
        Node* it; while (it=list_head) {list_head=it->next; delete it;}
     
        return 0;
    }

    参考:

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

    https://www.cnblogs.com/huty/p/8516997.html

  • 相关阅读:
    SpringMVC 下载本地文件
    Spring MVC 自定义转换器
    Struts,Hibernate,Spring经典面试题
    SpingMVC 执行的流程
    SpringMVC 应用配置
    SpringMVC特点
    Struts2学习
    mysql免安装配置
    mysql免安装版设置密码
    《金色梦乡》金句摘抄(六)
  • 原文地址:https://www.cnblogs.com/lovebay/p/14278434.html
Copyright © 2020-2023  润新知