c++11并发与多线程【第十节】

第十节 future其他成员函数、shared_future、atomic

学习视频

一、std::future的其他成员函数

std::future_status有三种状态

#include<iostream>
#include<vector>
#include<thread>
#include<string>
#include<list>
#include<mutex>
#include <future>

using namespace std;

//线程入口函数
int mythread(){
    cout<<"mythread() start "<<"thread id = "<<std::this_thread::get_id()<<endl;
    std::chrono::milliseconds dura(5000);//休息5秒钟
    std::this_thread::sleep_for(dura);//休息
    cout<<"mythread() end "<<"thread id = "<<std::this_thread::get_id()<<endl;
    return 5;
}

int main(){
    cout<<"main thread id = "<<std::this_thread::get_id()<<endl;
    std::future<int> result=std::async(std::launch::deferred,mythread);//自动创建一个新线程,使用mythread()作为入口函数
    cout<<"continue  ...."<<endl;

    std::future_status status = result.wait_for(std::chrono::seconds(6));//等待一秒
    if(status==std::future_status::timeout){//超时,表示异步创建的线程还没执行完
        cout<<"Time Out"<<endl;
    }
    else if(status==std::future_status::ready)//线程已经执行完成
    {    
        cout<<"THREAD RETURN Get Result"<<endl;
        cout<<result.get()<<endl;//获取线程的执行结果
    }
    else if(status==std::future_status::deferred){//如果async的第一个参数被设置为std::launch::deferred,此时延迟了,则本条件成立
        cout<<"THREAD Delay!!"<<endl;
        cout<<result.get()<<endl;//此时线程入口函数才会执行,此时不会创建新线程,而是都在主线程中执行
    }
    cout<<"i love china"<<endl;
    return 0;
}

二、std::shared_future

std::shared_future也是个类模板,std::future::get()使用移动语义转移数据获取值,只能获取一次,再用一次get()会报错,因为get()的内部是移动语义,直接把数据移出,然后就空了,而std::shared_future::get()复制数据。

#include<iostream>
#include<vector>
#include<thread>
#include<string>
#include<list>
#include<mutex>
#include <future>

using namespace std;

int mythread(int mypar){
    cout<<"args = "<<mypar<<endl;
    cout<<"mythread() start "<<"thread id = "<<std::this_thread::get_id()<<endl;
    std::chrono::milliseconds dura(5000);//休息5秒钟
    std::this_thread::sleep_for(dura);//休息
    cout<<"mythread() start "<<"thread id = "<<std::this_thread::get_id()<<endl;
    return 5;
}

void mythread2(std::shared_future<int> &tmpf){
    cout<<"mythread2() start  thread ID = "<<std::this_thread::get_id()<<endl;
    auto result =tmpf.get();//
    cout<<"mythread2() result = "<<result<<endl;
    return;
}

int main(){

    cout<<"main thread id = "<<std::this_thread::get_id()<<endl;
    std::packaged_task<int (int)> mypack(mythread);//packaged_task模板对象是int (int)即入口函数mythread的返回值和参数都是int,在这里把函数mythread通过packaged_task包装起来
    std::thread t1(std::ref(mypack),1);//线程直接开始执行,第二个参数作为线程入口函数的参数
    t1.join();//等待线程执行完毕
    std::future<int> result =mypack.get_future();//std::future对象里含有线程入口函数的返回结果,这里result保存mythread
    
    //std::shared_future<int> result_s(std::move(result));//
    bool ifcanget=result.valid();//可通过result.valid判断result数据是否还在
    std::shared_future<int> result_s(result.share());//两种写法意义相同,result.share()同样使用移动语义把数据移动到result_s
    ifcanget=result.valid();//此时ifcanget为false
    auto mythreadresult =result_s.get();
    mythreadresult=result_s.get();//可以执行多次get(),因为是复制数据

    std::thread t2(mythread2,std::ref(result_s));
    t2.join();
    return 0;
}

除此之外可以通过get_future返回值直接构造一个shared_future对象, std::shared_future<int> result_s(mypack.get_future());


三、原子操作std::atomic

3.1 原子操作引出范例

#include<iostream>
#include<vector>
#include<thread>
#include<string>
#include<list>
#include<mutex>
#include <future>

using namespace std;

int g_mycout=0;//定一个全局变量
std::mutex my_mutex;//互斥量
void mythread(){
    for(int i=0;i<10000;i++){
        //my_mutex.lock();//放在循环内
        g_mycout++;
        //my_mutex.unlock();
    }
    return;
}

int main(){

    thread mytobj1(mythread);
    thread mytobj2(mythread);
    mytobj1.join();
    mytobj2.join();
    cout<<"finally g_mycount == "<<g_mycout<<endl;
    return 0;
}

两个线程对同一个变量进行计算,如果不使用互斥量加锁的话,g_mycout 在加一的过程中可能会被另外一个线程打断,然后导致最后结果出错。

除了使用互斥量之外,还可以使用原子操作来解决。使用原子操作的效率要高于互斥量加锁解锁的方式,例如在上面的例子中,互斥量在循环内意味着要执行10000次加锁解锁,两个线程的话就是20000次。

原子操作可以理解成一种:不需要使用互斥量加锁(无锁)技术的多线程并发编程方式。
原子操作是在多线程中不会被打断的程序执行片段。原子操作在单行代码上,比互斥量在效率上高。互斥量的作用范围一般是一段代码。

原子操作一般指的是不可分割的操作,这种操作要么是完成的,要么是未完成的。

std::atomic 代表原子操作,是个类模板,用来封装某个类型的值。

using namespace std;

std::atomic<int> g_mycout(0);//封装了一个类型为int的对象,这是一个可以执行原子操作的整型变量
//std::mutex my_mutex;//互斥量
void mythread(){
    for(int i=0;i<10000;i++){
        //my_mutex.lock();//放在循环内
        g_mycout++;//现在的操作是原子操作,不会被打断
        //my_mutex.unlock();
    }
    return;
}

int main(){

    thread mytobj1(mythread);
    thread mytobj2(mythread);
    mytobj1.join();
    mytobj2.join();
    cout<<"finally g_mycount == "<<g_mycout<<endl;
    return 0;
}

其他案例:

std::atomic<bool> g_if_end(false);//线程退出标记,执行原子操作的bool类型变量
void mythread(){
    std::chrono::milliseconds dura(1000);//一秒钟
    while(g_if_end==false){
        //系统没要求退出,线程可以干自己的工作
        cout<<"运行中 thread ID = "<<std::this_thread::get_id()<<endl;
        std::this_thread::sleep_for(dura);
    }
    cout<<"thread ID = "<<std::this_thread::get_id()<<"结束"<<endl;
}
int main(){

    thread mytobj1(mythread);
    thread mytobj2(mythread);
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    g_if_end=true;//对原子对象的写操作,让线程结束
    mytobj1.join();
    mytobj2.join();
    cout<<"程序执行完毕"<<endl;
    
    return 0;
}

运行中 thread ID = 140737348093696
运行中 thread ID = 140737339700992
运行中 thread ID = 140737348093696
运行中 thread ID = 140737339700992
运行中 thread ID = 140737348093696
运行中 thread ID = 140737339700992
运行中 thread ID = 140737348093696
运行中 thread ID = 140737339700992
运行中 thread ID = 140737348093696
运行中 thread ID = 140737339700992
thread ID = 140737348093696结束
thread ID = 140737339700992结束
程序执行完毕

3.2 总结

原子操作一般用于计数或统计(累计发出去了多少数据包,或接受)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值