c++11并发与多线程【第十一节】std::atomic续谈、std::async深入谈

第十一节 std::atomic续谈、std::async深入谈

一、原子操作std::atomic续谈?

上一篇文章里,我们使用原子操作使得线程内的g_mycout自增++不会被其他线程打断,进而获取正确的结果:

#include<iostream>
#include<vector>
#include<thread>
#include<string>
#include<list>
#include<mutex>
#include <future>
using namespace std;
std::atomic<int> g_mycout(0);//封装了一个类型为int的对象,这是一个可以执行原子操作的整型变量
void mythread(){
    for(int i=0;i<10000000;i++){
        g_mycout++;
    }
    return;
}
int main(){
    thread mytobj1(mythread);
    thread mytobj2(mythread);
    mytobj1.join();
    mytobj2.join();
    cout<<"程序执行完毕, result = "<<g_mycout<<endl;
    return 0;
}

程序执行完毕, result = 20000000

如果将g_mycout++改为g_mycout=gmycout+1结果是否相同?

void mythread(){
    for(int i=0;i<10000000;i++){
        g_mycout=g_mycout+ 1;
        //g_mycout++;
    }
    return;
}

程序执行完毕, result = 19897970

此时结果是错误的。一般的原子操作,针对++,–,+=,&=,|=,*=,-=是支持的,其他的可能不支持。如果使用时不确定是否支持,则需要自己像上面一样进行测试。

二、std::async深入谈

2.1 std::async参数详述,async用来创建一个异步任务

int mythread(){
    cout<<"thread ID = "<<std::this_thread::get_id()<<"结束"<<endl;
    return 1;
}
int main(){
    cout<<"MAIN thread ID = "<<std::this_thread::get_id()<<endl;
    std::future<int> result=std::async(mythread);
    cout<<result.get()<<endl;
    return 0;
}

之前使用过的参数,std::launch::deferred【延迟调用】,以及std::launch::async【强制创建一个线程】;
std::thread()如果系统资源紧张,可能创建线程会失败,那么执行std::thread()时整个线程会崩溃。
std::async()我们一般不叫创建线程,一般叫做创建一个异步任务;
std::asyncstd::thread明显的不同就是async有时候并不创建线程;

  1. 使用std::launch::deferred来调用async
std::future<int> result=std::async(std::launch::deferred,mythread);
int main(){
    cout<<"MAIN thread ID = "<<std::this_thread::get_id()<<endl;
    std::future<int> result=std::async(std::launch::deferred,mythread);
    cout<<result.get()<<endl;
    return 0;
}

MAIN thread ID = 140737348093760
thread ID = 140737348093760结束
1

此时没有创建新线程。deferred延迟调用,并且不创建新线程,延迟到future对象调用get()或者wait()时才执行mythread();
2. 如果使用的是std::launch::async,强调这个异步任务在新线程上运行,则必须要创建出一个新线程。

std::future<int> result=std::async(std::launch::async,mythread);

MAIN thread ID = 140737348093760
thread ID = 140737348089600结束
1

  1. 同时使用std::launch::deferred|std::launch::async
std::future<int> result=std::async(std::launch::async|std::launch::deferred,mythread);

MAIN thread ID = 140737348093760
thread ID = 140737348089600结束
1

看上去和std::launch::async差不多,其实不一样。这个 | 意味着调用async的行为可能是创建新线程并立即执行,也可能是不创建新线程并延迟调到调用future类的get()才执行。具有不确定性。

  1. 不带参数,只给std::async()一个入口函数名
std::future<int> result=std::async(mythread);

第九节中,有错误,当不带参数时,他说默认值是std::launch::async 是错误的,默认值应该是std::launch::deferred|std::launch::async ,换句话说系统会自行决定是异步(创建新线程)还是同步运行(不创建新线程)。那么如何自行决定?看下面

2.2 std::async与std::thread的区别

std::thread创建线程,如果系统资源紧张,创建线程失败。整个程序就会崩溃。std::thread创建线程的方式,如果想接受线程返回值,只能用全局变量。
std::async创建异步任务,可以创建新线程也可以不创建。并且async调用方法很容易拿到线程入口函数的返回值。
由于系统资源限制:
如果用std::thread创建的线程太多,再用std::thread可能会失败,系统报告异常崩溃。
如果用std::async,一般就不会报异常崩溃,因为系统资源紧张时无法创建新线程时std::async这种不加额外参数的调用,就不会创建新线程,而是后续谁调用future类的get()或者wait(),谁就执行那个入口函数,即异步任务就运行在执行get()的语句的线程上。

如果强制std::async一定要创建新线程,那么就必须使用std::launch::async参数,代价就是系统资源紧张时可能崩溃。

经验:一个程序里,线程数量不宜超过100-200。

2.3 std::async的不确定性的解决

不加额外参数的std::async调用,让系统自行决定是否创建新线程,问题在于std::launch::deferred | std::launch::async 这种写法,这个异步任务到底有没有被推迟执行,还是创建了新线程。可以使用条件判断的方法来确定。

int mythread(){
	//执行一些操作
    cout<<"thread ID = "<<std::this_thread::get_id()<<"结束"<<endl;
    return 1;
}
int main(){
    cout<<"MAIN thread ID = "<<std::this_thread::get_id()<<endl;
    std::future<int> result=std::async(std::launch::async|std::launch::deferred,mythread);
    std::future_status status= result.wait_for(std::chrono::seconds(0));
    if(status==std::future_status::deferred)
    {
        //线程被推迟执行了,系统资源紧张了,采用延迟调用
        cout<<result.get()<<endl;
    }
    else if(status==std::future_status::ready){
        //任务没有被推迟执行,创建新线程
        //且线程执行成功并返回
        cout<<result.get()<<endl;//获取线程返回值
    }
    else if(status==std::future_status::timeout){
        //超时线程还没执行完(如果在线程入口函数里执行的时间较长)
        cout<<"超时线程还没执行完"<<endl;
        cout<<result.get()<<endl;
    }    
    return 0;
}

文章内容来源《C++并发与多线程视频课程》

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lk
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信