zoukankan      html  css  js  c++  java
  • 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() 函数就声明在此头文件中。

    无参函数:

    #include <iostream>
    #include <thread>  //多线程头文件
    #include <chrono>
    
    void hello() {
        for (int i = 0; i < 30; i++) {
            std::cout << "子线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(2));  //本线程休眠2秒
        }
    }
    
    int main() {
        std::thread t(hello);//创建线程并启动
        //t  线程名
        //参数:线程要执行的函数名--无参函数
        t.join();  //t线程结束,再往下执行
        
        
        for (int i = 0; i < 30; i++) {
            std::cout << "主线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(2));  
        }
    
        return 0;
        
    }

    有参函数 匿名函数  类成员函数:

    #include <iostream>
    #include <thread>  
    #include <chrono>
    
    void func1()
    {
        for (int i = 0; i != 10; ++i)
        {
            std::cout << "thread 1 print " << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1)); 
        }
    }
    
    void func2(int n)
    {
        for (int i = 0; i != 10; ++i)
        {
            std::cout << "thread 2 print " << n << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
    
    class A {
    public:
        void print() {
            for (int i = 0; i != 10; ++i)
                std::cout << "类成员函数:" <<i<< std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        
        }
    };
    
    int main() {
        std::thread t1(func1);
        std::thread t2(func2, 111); //有参函数
        //参数2:函数的参数
    
        std::thread t3([]()->void{std::cout << "匿名函数" << std::endl; });  //使用匿名函数
    
        A a;
        std::thread t4(&A::print, &a);   //使用类的普通成员函数
        //A  类名
        //print  函数名
        // a   类A的对象
        
        t1.join();
        t2.join();
        
        for (int i = 0; i < 30; i++) {
            std::cout << "主线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));  
        }
    
        return 0;
        
    }

    形参是引用的函数:

    #include <iostream>
    #include <thread>  //多线程头文件
    #include <chrono>
    
    void fl(int& x) {
        for (int i = x - 1; i > 0; i--) {
            std::cout << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
    
    int main() {
        int a = 20;
        //std::thread t(fl, a);  错
        //提示错误:未能使函数模板“unknown-type std::invoke(_Callable &&,_Ty1 &&,_Types2 &&...) noexcept(<expr>)”
        //错误原因:即使默认参数是引用形式,也会拷贝到线程独立内存中
        //解决办法:需要std::ref将参数转换为引用的形式----不同线程之间传递引用
        std::thread t(fl, std::ref(a));  //对   形参是引用的函数
    
        
        for (int i = 0; i < 30; i++) {
            std::cout << "主线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));  
        }
    
        return 0;
        
    }

    joinable()

    #include <iostream>
    #include <thread>  
    #include <chrono>
    
    void hello() {
        for (int i = 0; i < 30; i++) {
            std::cout << "子线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));  
        }
    }
    
    int main() {
        std::thread t(hello);
        bool b = t.joinable();  //返回能否调用join()函数
        //每个线程join()函数只能调用一次
        if (b) {
            std::cout << "b=" << b << std::endl;
            t.join();
        }
          
        
    
    
        for (int i = 0; i < 30; i++) {
            std::cout << "主线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(2));
            b = t.joinable();
            std::cout << "b=" << b << std::endl;
        }
    
        return 0;
    
    }

    转移线程所有权【控制权】move 

    #include <iostream>
    #include <thread>  
    #include <chrono>
    
    void hello() {
        for (int i = 0; i < 30; i++) {
            std::cout << "子线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));  
        }
    }
    
    int main() {
        std::thread t(hello);
        std::thread t1;
        t1= move(t);  //转移线程所有权【控制权】
        //hello函数到t1线程执行,t线程变成空线程
        
    
    
        for (int i = 0; i < 30; i++) {
            std::cout << "主线程:" << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(2));
          }
    
        return 0;
    
    }

    thread::hardware_concurrency 获取硬件支持并发线程数

        unsigned int in = std::thread::hardware_concurrency();//返回支持的并发线程数,若值非良定义或不可计算,则返回 ​0
        std::cout << in << std::endl;

    用户创建的线程数最好是:线程数目 = cpu核数+1

    获取线程id 

    #include <iostream>
    #include <thread>  
    #include <chrono>
    
    void hello() {
        for (int i = 0; i < 30; i++) {
            std::thread::id d1 = std::this_thread::get_id();
            std::cout << "子线程id=" << d1 << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));  
    
        }
    }
    
    int main() {
        std::thread::id d= std::this_thread::get_id(); //获取当前线程id
        std::cout <<"主线程id=" <<d << std::endl;
    
        std::thread t(hello);
        std::thread::id d2=t.get_id();    //获取指定线程id
    
        std::cout << "主线程获取子线程id=" << d2 << std::endl;
    
        t.join();
    
        return 0;
    
    }

    detach线程分离

    #include <thread>
    #include <iostream>
    #include <chrono>
    using namespace std;
    
    void func()
    {
        cout << "子线程func开始执行!" << endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
        cout << "子线程func执行结束!" << endl;
    }
    
    int main()
    {
        cout << "主线程main开始执行!" << endl;
        thread t(func);
        t.detach();  //指定线程与主线程分离 
        /*
        detach()的作用是将子线程和主线程的关联分离,也就是说detach()后
        子线程在后台独立继续运行,主线程无法再取得子线程的控制权,
        主线程结束,子线程也结束 
        */
        cout << "主线程main执行结束!" << endl;
        
        return 0;
    }

    pthread_exit(NULL) ; //退出主线程

    #include <thread>
    #include <iostream>
    #include <chrono>
    using namespace std;
    
    void func()
    {
        cout << "子线程func开始执行!" << endl;
        for(int i=0;i<30;i++){
            cout<<"i="<<i<<endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        cout << "子线程func执行结束!" << endl;
    }
    
    int main()
    {
        cout << "主线程main开始执行!" << endl;
        thread t(func);
        cout << "主线程main执行结束!
    " ;
        pthread_exit(NULL) ; //退出主线程 
        //这条语句的作用:主线程结束后,子线程继续执行 
        //仅仅是主线程结束,进程不会结束,进程内的其他线程也不会结束,直到所有线程结束,进程才会终止
        
        return 0;
    }

    互斥锁mutex

    需要:#include <mutex>

    方法一:lock  unlock方法--手动锁 

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;  //创建互斥锁对象
    
    void func(const std::string& str) {
        for (int i = 0; i < 30; i++) {
            mu.lock();  //上锁,防止别的线程进入
            //这种方法的缺陷:如果这句话抛出异常,mu永远会被锁住,所以不推荐
            std::cout << "子线程:" << str<<"--->"<<i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            mu.unlock();  //解锁,别的线程可以进入了
        }
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        std::thread t1(func, "t1线程");
        std::thread t2(func, "t2线程");
        std::thread t3(func, "t3线程");
        std::thread t4(func, "t4线程");
        t.join();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    
        return 0;
    
    }

    方法二:lock_guard方法--作用域锁 

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;
    
    void func(const std::string& str) {
        for (int i = 0; i < 30; i++) {
            std::lock_guard<std::mutex> guard(mu); //上锁,防止别的线程进入
            //当此句异常,mu对象自动被解锁
            //其原理是:声明一个局部的lock_guard对象,在定义该局部对象的时候加锁,出了该对象作用域的时候解锁
            //在需要被加锁的作用域内 将mutex传入到创建的std::lock_guard局部对象中
            std::cout << "子线程:" << str<<"--->"<<i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            
        }
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        std::thread t1(func, "t1线程");
        std::thread t2(func, "t2线程");
        std::thread t3(func, "t3线程");
        std::thread t4(func, "t4线程");
        t.join();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    
        return 0;
    
    }

    方法三:unique_lock

    这种方法的第二参数有三个选项

    第二参数:defer_lock

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;
    
    void func(const std::string& str) {
        std::unique_lock<std::mutex> locker(mu, std::defer_lock);//创建unique_lock锁对象
            /*
            locker: 是unique_lock的锁对象名
            参数1:互斥锁对象名
            参数2:3个选其一
                1.std::defer_lock:互斥锁还没有上锁,如果已经上锁报错
                有两个成员函数来进行加锁或解锁:lock(),加锁    unlock(),解锁
                可以在不同的地方多次加锁和解锁--比较灵活
            */
    
        for (int i = 0; i < 30; i++) {
            
            locker.lock();//上锁
            for (int j = 0; j < 5; j++) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "子线程--上锁:" << str << "--->" << j << std::endl;
            }
            
            locker.unlock();  //解锁
            for (int j = 0; j < 10; j++) {
                std::this_thread::sleep_for(std::chrono::seconds(5));
                std::cout << "子线程--不上锁:" << str << "--->" << j << std::endl;
            }
            
        }
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        std::thread t1(func, "t1线程");
        std::thread t2(func, "t2线程");
        std::thread t3(func, "t3线程");
        std::thread t4(func, "t4线程");
        t.join();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    
        return 0;
    
    }

    第二参数:adopt_lock

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;
    
    void func(const std::string& str) {
        mu.lock();
        std::unique_lock<std::mutex> lckkkk(mu, std::adopt_lock); //格式一
        //std::lock_guard<std::mutex> lckkkk(mu, std::adopt_lock);//把lock()转为lock_guard【作用域锁】--格式二
        //后面的意思与lock_guard【作用域锁】完全相同;注意:要提前用lock()锁定
        //lckkkk  【随便起名】
    
    
        for (int j = 0; j < 15; j++) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "子线程:" << str << "--->" << j << std::endl;
        }
                   
      
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        std::thread t1(func, "t1线程");
        std::thread t2(func, "t2线程");
        std::thread t3(func, "t3线程");
        std::thread t4(func, "t4线程");
        t.join();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    
        return 0;
    
    }

    第二参数:try_to_lock

    方式一

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;
    
    void func(const std::string& str) {
        std::unique_lock<std::mutex> lckkkk(mu, std::try_to_lock); //尝试用lock去锁定
        //会尝试用lock()去锁定,但如果没有锁定成功,也会立即返回,并不会阻塞在那里
        //用这个try_to_lock的前提是你自己不能先lock
        //lckkkk  【随便起名】a
       
        
    
        if (lckkkk.owns_lock()) {  
            //如果锁定了
            for (int j = 0; j < 15; j++) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "子线程:" << str << "--->" << j << std::endl;
            }
        }
        else
        {
            std::cout << str << "没锁定" << std::endl;
        }
                      
      
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        
        t.join();
        
    
        return 0;
    
    }

    方式二

    #include <iostream>
    #include <thread>  
    #include <chrono>
    #include <mutex>  
    #include <string>
    
    std::mutex mu;
    
    void func(const std::string& str) {
            
        std::unique_lock<std::mutex> lckkkk(mu, std::defer_lock);
        
        if (lckkkk.try_lock() == true) {
            //尝试用lock去锁,返回true表示拿到锁了
            for (int j = 0; j < 15; j++) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "子线程:" << str << "--->" << j << std::endl;
            }
        }
        else
        {
            std::cout << str << "没锁定" << std::endl;
        }
        
    }
    
    int main() {
        
        std::thread t(func,"t线程");
        
        t.join();
        
    
        return 0;
    
    }

    线程间数据共享future 

    线程间数据共享一般可以使用全局变量,但是从安全角度看,有些不妥;为此C++11提供了std::future类模板 

    async()、get()

    #include <future>  //这个类已经封装了多线程
    #include <iostream>
    #include <chrono>
    
    int func1(int x)  //线程函数
    {
        
        std::this_thread::sleep_for(std::chrono::seconds(4));
        std::cout << "func1" << std::endl;
        return x + 100;  //线程函数的返回值会保存到future对象
    }
    
    
    int main()
    {
        
        std::future<int> fut;  //创建future对象
        /*
        <int>   是线程函数返回值的类型,就是要获取的数据类型
        */
    
        fut = std::async(func1, 86);  //创建线程
        //任务创建之后,std::async立即返回一个std::future对象
        //参数1:线程要执行的函数名
        //参数2:传递给线程函数的实参
        
        int ret = fut.get(); //获取线程函数的返回值
        //如果线程函数还在任务中,会处于阻塞状态,等待线程函数的返回值
        std::cout << "ret="<<ret << std::endl;
        
    
        return 0;
    }

    wait_for()

    #include <future> 
    #include <iostream>
    #include <chrono>
    
    int func1(int x) 
    {
        std::cout << "func1" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(30));
        std::cout << "func1" << std::endl;
        return x + 100;  
    }
    
    
    int main()
    {
        
        std::future<int> fut;  
        
        fut = std::async(func1, 86);  
        
        std::chrono::seconds ms(1);
    
        //std::future_status st = fut.wait_for(ms);  //等待线程函数的返回值
        /*
        参数:最多等待时间,超过这个时间继续往下执行
       在指定时间内一旦线程函数返回值了,立即往下执行
        注意:参数是std::chrono对象
        返回值:在指定时间内线程函数返回值了,则返回std::future_status::ready状态
        在指定时间内线程函数没有返回值,则返回std::future_status::timeout状态
      也可以用wait()函数,但是不能设置等待时间
      也可以wait_until(等待直到某特定时间点到达)
    */ while (fut.wait_for(ms)== std::future_status::timeout) { std::cout << "."; } std::cout << std::endl; std::cout<<"线程函数完成任务了"<< std::endl; return 0; }

    promise

    #include <future> 
    #include <iostream>
    #include <chrono>
    #include <thread> 
    
    void func(std::future<int>& fut) {
        
        int x = fut.get();   // 等待直到获取共享状态的值
        std::cout << "x=" << x << std::endl;
    }
    
    
    int main()
    {
        /*
        std::promise的作用就是提供一个不同线程之间的数据同步机制,
        它可以存储一个某种类型的值,并将其传递给对应的future对象, 
        即使这个future对象不在同一个线程中也可以安全的访问到这个值
        */
        std::promise<int> prom;        //创建promise对象
        //<int>  是共享数据的类型
        std::future<int> fut = prom.get_future();  //和future 关联
        std::thread t(func, std::ref(fut)); // 将 future 交给另外一个线程t
        std::this_thread::sleep_for(std::chrono::seconds(10));
        prom.set_value(10); // 设置共享状态的值, 这个值也自动传递给捆绑的future对象
        t.join();
    
        return 0;
    }

    std::packaged_task

    主要是调用包装函数的

    #include <iostream>
    #include <future>
    #include <chrono>
    #include <functional>
     
    int Test_Fun(int a, int b, int &c)
    {
        std::this_thread::sleep_for(std::chrono::seconds(5));
         c = a + b + 230;
         return c;
    }
     
    int main()
    {
        std::packaged_task<int(int, int, int&)> pt1(Test_Fun); //声明一个packaged_task对象pt1,并指向包装函数Test_Fun
        //<int(int, int, int&)    包装函数的返回值类型和形参类型 
        
        std::future<int> fu1 = pt1.get_future();  //future对象fu1,并与pt1关联
        //共享数据类型就是包装函数返回值类型 
     
        int c = 0;
     
        std::thread t1(std::move(pt1), 1, 2, std::ref(c)); //创建一个线程t1,执行pt1函数 
        
        int iResult = fu1.get(); //等待线程函数返回共享数据 
     
        std::cout << "执行结果:" << iResult << std::endl;    
        std::cout << "c:" << c << std::endl;    
     
        return 0;
    }

    调用包装得到匿名函数

    #include <iostream>  
    #include <future>     
    #include <thread>   
    
    int main ()
    {
        std::packaged_task<int(int)> bar([](int x){return x*2;}); //创建packaged_task对象,并指向匿名函数 
    
        std::future<int> ret = bar.get_future();
    
        std::thread(std::move(bar), 10).detach(); // 产生线程,调用被包装的任务.
        
        int value = ret.get(); // 等待任务完成并获取结果.
        std::cout << "The double of 10 is " << value << ".
    ";
        
    return 0;
    }

    shared_future 

    #include <iostream>   
    #include <future>    
    
    int do_get_value() { return 10; }
    
    int main ()
    {
        std::future<int> fut = std::async(do_get_value);
        std::shared_future<int> shared_fut = fut.share();  //创建shared_future对象,并从future对象获取数据
        // 共享的shared_future对象可以被多次访问.
        //future对象只能访问一次,在那之后future对象就处于无效状态 
        //获取数据后, fut变为无效 
         bool b=fut.valid();
        if (b){
            std::cout << "有效的"  << '
    ';
        }
        else{
            std::cout << "无效的"  << '
    ';
        } 
        
        
        std::cout << "value: " << shared_fut.get() << '
    ';  //第一次访问 
        std::cout << "its double: " << shared_fut.get()*2 << '
    ';  //第二次访问 
       
         
    
        return 0;
    }

    valid()返回future对象是否有效 

    #include <iostream>       // std::cout
    #include <future>         // std::async, std::future
    
    int do_get_value() { return 11; }
    
    int main ()
    {
        std::future<int> foo,bar;  //初始化之前是无效的 
        
        bool b=foo.valid(); //返回future对象是否有效
            
        if (b){
            std::cout << "foo是:初始化之前有效的"  << '
    ';
        }
        else{
            std::cout << "foo是:初始化之前无效的"  << '
    ';
        } 
            foo = std::async(do_get_value);  //初始化后变为有效 
         b=foo.valid(); 
            
        if (b){
            std::cout << "foo是:初始化之后有效的"  << '
    ';
        }
        else{
            std::cout << "foo是:初始化之后无效的"  << '
    ';
        } 
        
        bar=std::move(foo);  //转移所有权
        //foo 变为无效    bar变为有效 
        
        b=foo.valid(); 
            
        if (b){
            std::cout << "foo是:所有权转移之后有效的"  << '
    ';
        }
        else{
            std::cout << "foo是:所有权转移之后无效的"  << '
    ';
        } 
        b=bar.valid(); 
            
        if (b){
            std::cout << "bar是:所有权转移之后有效的"  << '
    ';
        }
        else{
            std::cout << "bar是:所有权转移之后无效的"  << '
    ';
        } 
        
         
    
        return 0;
    }

  • 相关阅读:
    FusionCharts的类
    FusionCharts图表控件中文版使用手册
    java Integer
    java --final关键字
    HTTP缓存机制及原理
    java颜色代码对照表
    centos svn 服务器间的数据迁移
    tp3.2 URL_MODEL为2 配置
    order by group by
    jpgraph 折线图--解决中文乱码的问题(标题和图例)
  • 原文地址:https://www.cnblogs.com/liming19680104/p/13532826.html
Copyright © 2011-2022 走看看