《白话C++》第12章并发,Page521 异步概念和示例

两个计算函数:

#include <iostream>
#include <future> //async, future

#include "MyCPUTimer.hpp"

using namespace std;

int calc_1()
{
    long r = 0;
    for(int t = 1; t <= 10; ++t)
    {
        for(int i = -999990; i <= 999990; ++ i)
            r += i;
        r /= t;
    }

    return r;
}

int calc_2()
{
    long r = 0;
    for(int t = 1; t <= 10; ++t)
    {
        for(int i = -999990; i <= 999990; ++i)
            r -= i;
        r /= t;
    }

    return r;
}

我们做第一个测试,串行调用二者,并输出用时,计时器就是用第10章《STL和boost》“日期和时间”小节中,我们自行实现的计时器。当然,也可以引入boost相关的库,直接使用该库的cpu_timer工具类

void test1()
{
    MyCPUTimer timer(true);
    int sum = calc_1() + calc_2();
    timer.Stop();

    cout << sum << ", use " << timer.Elapsed() << endl;
}

 先执行calc_1(),再执行calc_2(),两只串行执行,用时一共108ms

如果并行执行呢?

C++11提供了两个关键组件用于支持如下功能:

一是当前线程不等某件事做完(甚至不等它是否开始),继续往下做事:标准库的asyn()函数模板

二是当前线程在后续某个时候等待前面事做完并得到结果:标准库的future数据类模板

async是“asynchronous(异步的)”的意思,简化后的“声明”如下:

future <T> async(Function&& f, args&&... args);

第一个入参f是指C++中的可执行体(比如函数指针、函数对象、lambda表达式、std::function对象等),后续可变个数的入参,是执行f时所需要的入参。

以calc_1()函数为例,由于该函数不需要入参,所以想要异步调用它,只需写“async(calc_1)”即可。

future是“未来”或“期货”的意思,它正是async()函数返回结果的数据类型。更好的理解是将它当做“存根”或“收条”理解,我们让async()去做某件事,但它不能立即返回做此事的结果,只是给我们一张“收条”,未来我们可以依据它来取得真正的结果。

以调用calc_1()这件事为例,该函数真正返回的结果是一个整数,所以需要为future类模板指定int作为模板入参:

future <int> future_1 = async(calc_1);

这行代码的解读是:我们真正想做的是调用calc_1()并得到它的结果,但我们不想等它执行,所以将它交给async()异步处理,async收到任务后给了我们一张收据,名为future_1,并且根据这张收条,将来可以得到int类型的数据(即calc_1()的执行结果)。从收条得到真正的结果,代码也很简单:

int result_1 = future_1.get();

就是在future对象上调用get()方法,此时如果async已经调用完calc_1(),就直接返回后者执行结果,如果还没有,就立即调用以便返回结果。

根据以上内容,写了test2();

void test2()
{
    MyCPUTimer timer(true);
    std::future <int> future_1 = std::async(calc_1);
    std::future <int> future_2 = std::async(calc_2);

    int sum = future_1.get() + future_2.get();
    timer.Stop();

    cout << sum << ", use " << timer.Elapsed() << endl;
}

可以看到,用时53毫秒,执行时间缩短了一半时间。

int main()
{
    test1();
    test2();

    return 0;
}

我们让两个此时,放到一个main中调用,反复执行

会发现,test1()的耗时较为稳定。相比之下,test2()耗时变化幅度较大,原因在于async比较任性,当我们执行语句“async(calc_1);”时,它会立即启动一个新的线程以便执行calc_1,但也可能因为外部资源紧张等原因而拖拖拉拉甚至无动于衷。因此代码应该尽量给各异步操作多一些时间,在逻辑允许的情况下,尽量迟一些调用future对象的get()成员。

我们让主线程,也参与到计算中去:

void test3()
{
    MyCPUTimer timer(true);
    //先交待下属干活
    std::future <int> future_1 = std::async(calc_1);

    //交待完,领导(调用者线程)别闲着,也干点活
    int r2 = calc_2();
    int sum = future_1.get() + r2;
    timer.Stop();

    cout << sum << ", use " << timer.Elapsed() << endl;
}

int main()
{
    test1();
    test2();
    test3();

    return 0;
}

运行结果如下:

作业:使用std::thread实现

将上述例子,使用std::thread实现,并思考它和使用“async()(异步调用)”实现有何异同?

#include <iostream>
#include "MyCPUTimer.hpp"
#include <thread>

using namespace std;

int num1, num2;

int calc_1()
{
    long r = 0;
    for(int t = 1; t <= 10; ++t)
    {
        for(int i = -999990; i <= 999990; ++i)
            r += i;
        r /= t;

    }

    num1 = r;
    return r;
}

int calc_2()
{
    long r = 0;
    for(int t = 1; t <= 10; ++ t)
    {

        for(int i = -999990; i <= 999990; ++i)
            r += i;
        r /= t;
    }

    num2 = r;
    return r;
}

int main()
{
    MyCPUTimer timer(true);

    thread trad_1(&calc_1);
    thread trad_2(&calc_2);

    trad_1.join();
    trad_2.join();

    int sum = num1 + num2;
    timer.Stop();

    cout << sum << ", use " << timer.Elapsed() << endl;

    return 0;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值