C++11之std::async使用介绍

在C++11中有个async异步函数,其声明如下:

template <class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type>
  async (launch policy, Fn&& fn, Args&&... args);

该模板函数 result_of的功能参见std::result_of/std::invoke_result用法总结 博文。async 异步地运行函数 fn 并返回最终将保存该函数调用结果的 std::future 中,其中policy策略有以下三种:

// 异步启动的策略  
enum class launch {  
    // 异步启动,在调用std::async()时创建一个新的线程以异步调用函数,并返回future对象;
    async = 0x1,
    // 延迟启动,在调用std::async()时不创建线程,直到调用了future对象的get()或wait()方法时,才创建线程;                    
    deferred = 0x2,  
     // 自动,函数在某一时刻自动选择策略,这取决于系统和库的实现,通常是优化系统中当前并发的可用性           
    any = async | deferred,      
    sync = deferred  
}; 
  • 参数 fn 是要调用的可调用 (Callable) 对象
  • 参数args 是传递给 f 的参数

异步调用函数可以大大提高程序运行的效率,验证如下:

辅助函数和声明

这个是验证程序的通用代码:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>
#include <mutex>
#include <memory>
#include <functional>
#include <future>

using namespace std;

typedef std::chrono::steady_clock                STEADY_CLOCK;
typedef std::chrono::steady_clock::time_point    TIME_POINT;

void print_diff_time(const TIME_POINT& t1, const TIME_POINT& t2)
{
    std::chrono::duration<double, std::milli> dTimeSpan = std::chrono::duration<double,std::milli>(t2-t1);

    std::cout << "time span : " << dTimeSpan.count() << "ms\n";  
}

原始程序

下面演示的是,最原始的程序计算代码,关键代码如下:

//计算A函数
int calculateA()
{
    //延迟1ms
    this_thread::sleep_for(std::chrono::milliseconds(1));
    return 1;
}

//计算B函数
int calculateB()
{
    //延迟2ms
    this_thread::sleep_for(std::chrono::milliseconds(2));
    return 2;
}

//计算C函数
int calculateC()
{
    //延迟3ms
    this_thread::sleep_for(std::chrono::milliseconds(3));
    return 3;
}

//统计三者的和
void test_common_time()
{
    TIME_POINT t1 = STEADY_CLOCK::now();
    
    int c = calculateA() + (calculateB() + calculateC());
    
    TIME_POINT t2 = STEADY_CLOCK::now();
    
    cout << "test_common_time result is :" <<  c << "  ";
    print_diff_time(t1, t2);
}

 运行结果:

test_common_time result is :6  time span : 6.55506ms

多线程计算

没有引入多线程计算,程序计算时间达6.5ms,相对效率不高,下面演示多线程计算,代码如下:

//防止数据竞争
std::mutex g_mtu;

void calA(int& sum)
{
    this_thread::sleep_for(std::chrono::milliseconds(1));
    //多线程加锁保护
    lock_guard<mutex> lk(g_mtu);
    sum += 1;
}

void calB(int& sum)
{
    this_thread::sleep_for(std::chrono::milliseconds(2));
    lock_guard<mutex> lk(g_mtu);
    sum += 2;
}

void calC(int& sum)
{
    this_thread::sleep_for(std::chrono::milliseconds(3));
    lock_guard<mutex> lk(g_mtu);
    sum += 3;
}

//case2
void test_threads_time()
{
    int sum = 0;
    
    TIME_POINT t11 = STEADY_CLOCK::now();
	//启动三个线程,进行运算
    thread t1(calA, ref(sum));
    thread t2(calB, ref(sum)); 
    thread t3(calC, ref(sum)); 
	//等待计算完成
    t1.join();
    t2.join();
    t3.join();
    //计算完成
    TIME_POINT t22 = STEADY_CLOCK::now();
    cout << "test_threads_time result is :" <<  sum << "  ";
    print_diff_time(t11, t22);
        
}

运行结果:

test_threads_time result is :6  time span : 3.57699ms

程序计算时间将近减少了一半,效率提高了很多,但是程序引入了锁机制,程序的实现相对复杂了。

异步调用

在C++11中引入了async异步调用函数,其封装了异步(多线程)实现的复杂过程,将计算结果保存在future<>中,通过get()获取每个对象的最终结果。代码如下:

//case3
void test_feture_time()
{
	int sum = 0;
	
	TIME_POINT t11 = STEADY_CLOCK::now();
	
	//异步调用
	future<int> f1 = std::async(calculateA);
	future<int> f2 = std::async(calculateB);
	future<int> f3 = std::async(calculateC);
	//get()函数会阻塞,直到结果返回
	sum = f1.get() + f2.get() + f3.get();
	
	TIME_POINT t22 = STEADY_CLOCK::now();
	
	cout << "test_feture_time result is :" <<  sum << "  ";
	
	print_diff_time(t11, t22);       
}

运行结果:

test_future_time result is :6  time span : 3.76372ms

相比多线程版本的计算过程,async异步调用整个程序更加简洁,没有明显引入线程,锁,缓冲区等概念。又比如:

std::future<bool> asyncFuncCall = std::async(std::launch::async, func, args...);

// 如果结果还未返回,那么就等待
 while(asyncFuncCall.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready)
{
    // 做其它的事
 }

总结

async异步调用函数通过启动一个新线程或者复用一个它认为合适的已有线程,async是语义上的并发即可,并关心具体的调用策略。

”简单性”是async/future设计中最重视的一个方面;future一般也可以和线程一起使用,不过不要使用async()来启动类似I/O操作,操作互斥体(mutex),多任务交互操作等复杂任务。async()背后的理念和range-for statement很类似:简单事儿简单做,把复杂的事情留给一般的通用机制来搞定。

原文链接:https://blog.csdn.net/c_base_jin/article/details/79506347

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,C++ 11 引入了 std::async 函数,可以用它来创建异步任务。std::async 函数的原型如下所示: ```c++ template <class Function, class... Args> std::future<typename std::result_of<Function(Args...)>::type> async(Function&& f, Args&&... args); ``` std::async 函数的作用是创建一个异步任务,并返回一个 std::future 对象,用于获取异步任务的返回值。std::async 函数的第一个参数是要执行的函数,后面的参数是该函数的参数。该函数会在一个新线程中执行,并在执行完成后返回结果。如果该函数抛出异常,则 std::future 对象中会保存该异常信息。 std::async 函数还可以指定执行策略,例如 std::launch::async 表示在新线程中执行异步任务,std::launch::deferred 表示在调用 std::future::get() 函数时执行异步任务。如果不指定执行策略,则由编译器自行决定。 以下是一个使用 std::async 函数创建异步任务的示例: ```c++ #include <iostream> #include <future> int foo(int x) { std::cout << "foo is running in thread " << std::this_thread::get_id() << std::endl; return x + 1; } int main() { std::future<int> result = std::async(std::launch::async, foo, 1); std::cout << "main is running in thread " << std::this_thread::get_id() << std::endl; std::cout << "result is " << result.get() << std::endl; return 0; } ``` 在上面的示例中,使用 std::async 函数创建了一个异步任务 foo,并将参数 1 传递给该函数。执行结果会保存在 std::future 对象中,可以使用 std::future::get() 函数获取异步任务的返回值。在主线程中也输出了一些信息,用于对比异步任务和主线程中的执行情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值