目录
介绍
异步与同步编程
在我们编程的过程中,经常会听到这样的名词,这里就对同步和异步作一个简单的解释:
同步编程
:同步编程,即是一种典型的请求-响应模型,当请求调用一个函数或方法后,需等待其响应返回,然后执行后续代码。
一般情况下,同步编程,代码按序依次执行,能很好的保证程序的执行,但是在某些场景下,比如读取文件内容,或请求服务器接口数据,需要根据返回的数据内容执行后续操作,读取文件和请求接口直到数据返回这一过程是需要时间的,网络越差,耗费时间越长。
如果使用同步编程,那么在等待返回结果的过程中,程序将阻塞在这里,如果是界面程序,无法刷新界面导致无响应,那将是致命的。因此,这里就需要使用异步编程了。
异步编程
:异步编程就是让耗时的函数异步执行,而不阻塞当前的线程。但我们想要获取执行结果怎么办,那我们通常需要使用回调函数来完成,使用回调却并不是那么好处理,因为是异步的,回调函数就必须知道关键的上下文才能知道这个结果要怎么使用,这也是异步编程的一大难点。
注意
:异步和多线程是有区别的,异步并不一定就是多线程,如消息队列,将要执行的任务添加到消息队列,然后并不是在添加任务时执行,而是等轮到它了才执行,它们可以在同一线程,这也是一种异步。
因此C++11提供的异步编程高级封装就显得格外的好用,std::async返回的std::future就用来获取其执行结果,十分方便。
std::async与std::future
std::async
- std::async是C++11才有的一个异步调用模板,它将异步地运行传入的函数并返回一个std::future。合理地使用,可以更轻松地完成异步操作。
- std::async是与std::future搭配使用的,但我们一般不直接使用std::future,而是使用对其封装的std::async,基本上可以代替std::thread完成所有任务。
- std::async实际上是封装了std::promise,std::packaged_task,std::thread和std::future,然后提供给我们的一个异步编程的高级封装,让我们无需考虑太多的细节。
std::future
std::future提供了访问异步操作结果的机制,让我们在异步操作中能方便安全的获取异步操作的结果。
详细介绍可见C++11之std::future对象使用说明
使用示例
简单使用
auto future = std::async(f,args)
:在没给出执行策略(policy)的情况下,f的执行取决于实现,即有可能是在新线程中执行,也有可能在调用future.get()
后在调用线程执行。因此最好是在使用时给出执行策略。
示例1-1
#include <iostream>
#include <future>
void print(int n) {
for (int i = 0; i < n; i++) {
std::cout << ".";
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << std::endl;
}
int main(){
auto future = std::async(print, 5);
future.get(); // 这句可以不写,可见注意事项第一点
return 0; // 结果:.....
}
示例1-2
std::future有如下函数:
此处测试使用wait_for
:
示例2:1-n分部求和
举个简单的栗子,来说明async的用处之一:分部求和,当一个函数可以拆成几部分互不影响的执行时,就可以让其多个线程来异步处理,以加快处理速度。
- 普通的求和函数:
int64_t Sum(int64_t from, int64_t to) { int64_t ret = 0; for (; from <= to; ++from) { ret += from; } return ret; }
- 分部求和的函数:
int64_t Sum_with_MultiThread(int64_t from, int64_t to, size_t thread_num) { int64_t ret = 0; int64_t n = thread_num ? (to - from) / thread_num : (to - from); std::vector<std::future<int64_t>> v; for (; from <= to; ++from) { v.push_back(std::async(Sum, from, from + n > to ? to : from + n)); from += n; } for (auto &f : v) { ret += f.get(); } return ret; }
让我们写出测试代码:
#include <iostream>
#include <future>
int main(){
std::cout << Sum(1, 1000000000) << std::endl;
std::cout << Sum_with_MultiThread(1, 1000000000, 4) << std::endl; // 最多4线程,因为cpu只有4核
// TestTimeUsed是我写的测试用时的函数,后面给出
std::cout << Tools::TestTimeUsed(Sum_with_MultiThread, 1, 1000000000, 4) << std::endl; // 最多4线程,因为cpu只有4核
std::cout << Tools::TestTimeUsed(Sum, 1, 1000000000) << std::endl;
return 0;
}
运行结果:可以看出4个线程计算差不多就是四分之一的时间。