C++标准库(第二版,作者_NicolaiMJosuttis)_第18章并发_18.1async1

  • Post author:
  • Post category:其他


对于初学者而言,“以多线程运行程序”的最佳起点就是C++标准库中由std::sync()和class std::future<>提供的高级接口:

  • async()提供一个接口,让一段功能或者说一个callable object 若是可能的话在后台运行,成为一个独立的线程。
  • Class future<>允许你等待线程结束并获取其结果(一个返回值或者一个异常)

下面的这个例子是需要计算两个操作数的和,而这两个数是两个函数的返回值。

// async1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <future>
#include <thread>
#include <chrono>
#include <random>
#include <iostream>
#include <exception>
using namespace std;

int doSomething(char c)
{
    //random-number generator(use C as seed to get different sequence)
    std::default_random_engine dre(c);
    std::uniform_int_distribution<int> id(10,1000);
    //每隔一会就循环打印
    for (int i = 0; i < 10; ++i) {
        this_thread::sleep_for(chrono::milliseconds(id(dre)));
        cout.put(c).flush();
    }
    return c;
}
int func1()
{
    return doSomething('.');//这里要记住:func1() 传入的是 点点,点点,点点
}
int func2()
{
    return doSomething('+');//这里要记住:func1() 传入的是 加号,加号,加号
}

int main()
{
    std::cout << "starting func1() in background" << " and func2() in foreground:" << std::endl;
    //start func1() asynchronously(now or later or never)
    std::future<int> result1(std::async(func1));

    int result2 = func2();//call func2() synchronously (here and now )

    //print result(wait for func1() to finish and add its result to result2)
    int result = result1.get() + result2;

    std::cout << "\nresult of func1()+func2(): " << result << std::endl;
   
    
}


这里啊读者先不用去纠结doSomething()函数的 具体实现,你就知道它做点事情返回int 就可以了。

传统的实现就是int result=func1()+func2();现在需要用多线程的方式来并行处理,就可以这么写:

//start func1() asynchronously(now or later or never)
    std::future<int> result1(std::async(func1));

    int result2 = func2();//call func2() synchronously (here and now )

    //print result(wait for func1() to finish and add its result to result2)
    int result = result1.get() + result2;

在这里async()尝试将其所获得的函数立刻异步启动于一个分离的线程内。因此概念上func1() 在这里被启动了,不会造成main()的停滞。基于两个原因,返回future object 是必要的:

  1. 它允许你取得传给async()的那个函数的未来结果–也许是个返回值,也许是异常。
  2. 它必须存在,确保目标函数或快、或慢最终会被调用。

也可以用auto(并且是推荐用auto)来声明future object :

auto result1(std::async(func1));

接下来启动func2()于前台,这个就是正常的同步化的调用,于是程序在此停滞:

int result2=func2();

如果先前func1()成功地被async()启动并且尚未结束,现在func1()和func2()就是并行运作。

接下来处理总和。这就是需要func1()结果的时刻。为了获得它,需要使用get()

 int result = result1.get() + result2;

随着get()被调用,会发生下面三件事情当中的一个:

  1. 如果func1()被async()启动于一个分离线程中并且已经结束,你会立刻获得其结果。
  2. 如果func1()被async()启动但是并没有运行结束,get()会引发停滞(block),待func1()结束后获得结果。
  3. 如果func1()没有启动,会强迫启动如同一个同步调用;get()会引发停滞(block)直至产生结果。

调用async()并不保证传入的函数一定会被启动和结束。如果有个线程处于可用的状态,那么它的确会被启动,但如果不是这样(也许你的环境不支持多线程,或者也许当时没有线程可用),这一调用会被推迟至你明确说你需要结果的时候(也就是调用get()的时候)或只是希望目标函数完成其任务(也就是调用wait()的时候:我还没有懂这个wait的具体含义)。

因此这个程序有两种可能性:

  • 第一种:如果async()成功启动func1()(就是输出点点的那个函数),输出结果可能如下:
starting func1() in background and func2() in foreground:
++..++++.++.+.+.....
result of func1()+func2(): 89

上面的结果是作者在书中给出的,这个程序在我的机器上的运行结果是:

starting func1() in background and func2() in foreground:
.++...+...+..++.++++
result of func1()+func2(): 89

对于这个结果可以这么理解:

异步的线程启动了,打印输出了点点,

与此同时,同步的func2()也在运行,打印输出了+,有打印输出了+,这个时候新启动的线程也要输出结果,而且是输出了三个点点点。。。。。。

  • 如果async()无法启动func1(),后者会在func2()结束后且get()被调用的时候执行。就有了下面的输出:

    starting func1() in background and func2() in foreground:
    ++++++++++..........
    result of func1()+func2(): 89

    注意:如果没有调用get()就无法保证func1()一定被调用。如果没有那样一个明确的请求,即使main()终止造成程序结束,也不会唤醒后台线程。


为了获取最佳效果,一般而言你的目标应该是调用asyn()和调用get()之间的距离最大化

.



版权声明:本文为superfreak原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。