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;
}

原文地址:https://www.cnblogs.com/liming19680104/p/13532826.html