赞
踩
C++中提供了异步操作相关的类:
1. std::future: 异步结果的传输通道,可以很方便的获取线程函数的返回值。
在C++中,如果希望获取线程函数的返回值,就不能直接通过thread.join()得到结果,这时就必须定义一个变量,在线程函数中去给这个变量赋值,然后执行join,最后得到结果,这是一个非常繁琐的过程。C++11 的 thread 库提供了future,用来访问异步操作的结果。为什么会被命名为future呢:这是因为一个异步操作的结果不能马上获取,只能在未来某个时候从某个地方获取,这个异步操作的结果是一个未来的期待值,所以被称为future,future相当于提供了一个获取异步操作结果的通道。可以通过同步等待的方式获取结果,也可以通过查询future的状态来获取异步操作的结果:
future的状态为 future_status , 共有三种状态:
1.Deferred: 异步操作还没有开始
2. Ready: 异步操作已经完成
3. Timeout: 异步操作超时
2.std::promise:
std::promise将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完之后,就可以通过pormise的future获取该值了。取值是间接的通过promise内部提供的future进行的。
具体的关系我个人理解为如下的图:
在线程函数的外部创建std::promise,然后将它作为线程函数的参数传入,在线程函数中为其赋值。然后在线程函数外部通过promise 的 get_future()方法创建 std::future,再通过future的get方法获取变量的值即可。所以从图中可以看出,future相当于异步结果的输出通道。而这个通道是位于std::promise内部的。
使用方法如下:
使用future的get方法,获得任务执行的返回值, 但是如果当前任务尚未执行, 任务会触发立即执行, 并且堵塞当前线程,直到任务完成
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
-
- void task(std::promise<int> &prom , int para) // promise作为函数的参数
- {
- int res = para * 10;
- prom.set_value_at_thread_exit(res); // 将线程中需要输出的值存放到promise中
- }
-
-
- int main()
- {
- std::promise<int> promise_; // 创建promise
- std::thread t1(task, std::ref(promise_), 12); // 将promise作为参数传入到线程函数中
- t1.join();
-
- std::future<int> f = promise_.get_future(); // 创建通道 通道输出数据的类型
- std::cout << "The task output " << f.get() << std::endl;
- return 0;
- }
当然,也可以在一个线程中执行线程函数,在拎一个线程中获取线程函数中需要输出的值
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
-
- void task(std::promise<int> &prom , int para) // promise作为函数的参数
- {
- int res = para * 10;
- prom.set_value_at_thread_exit(res); // 将线程中需要输出的值存放到promise中
- }
-
- void get_task_value(std::future<int> &future) // future作为函数的参数
- {
- std::cout << "The task output " << future.get() << std::endl;
- }
-
- int main()
- {
- std::promise<int> promise_; // 创建promise
- std::future<int> future = promise_.get_future(); // 创建通道
-
- std::thread t1(task, std::ref(promise_), 12); // 将promise作为参数传入到线程函数中 线程函数
- std::thread t2(get_task_value, std::ref(future)); // 获取线程函数值的线程
-
- t1.join();
- t2.join();
-
- return 0;
- }
可以通过查询future的状态来获取异步任务的执行情况,例如,可以在上面的代码中添加future的状态查询,直到任务完成为止。
future的状态为 future_status , 共有三种状态:
1.Deferred: 异步操作还没有开始
2. Ready: 异步操作已经完成
3. Timeout: 异步操作超时
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- void task(std::promise<int> &prom , int para) // promise作为函数的参数
- {
- std::this_thread::sleep_for(std::chrono::seconds(2)); // 线程延时2s
- int res = para * 10;
- prom.set_value_at_thread_exit(res); // 将线程中需要输出的值存放到promise中
- }
-
- void get_task_value(std::future<int> &future) // future作为函数的参数
- {
- std::future_status status;
- do
- {
- status = future.wait_for(std::chrono::milliseconds(200)); // 等待时间
- if (status == std::future_status::deferred)
- {
- // 异步操作还没有开始
- std::cout << "异步操作还没有开始" << std::endl;
- }
- else if (status == std::future_status::timeout)
- {
- // 异步操作超时, 就是还没有完成的意思
- std::cout << "异步操作超时" << std::endl;
- }
- else
- {
- // 异步操作已经完成
- std::cout << "The task output " << future.get() << std::endl;
- }
- } while (status != std::future_status::ready);
-
- //std::cout << "The task output " << future.get() << std::endl;
- }
-
- int main()
- {
- std::promise<int> promise_; // 创建promise
- std::future<int> future = promise_.get_future(); // 创建通道
-
- std::thread t1(task, std::ref(promise_), 12); // 将promise作为参数传入到线程函数中 线程函数
- std::thread t2(get_task_value, std::ref(future)); // 获取线程函数值的线程
-
- t1.join();
- t2.join();
-
- return 0;
- }
运行结果:
关于shared_future:
使用与futrue相似,shared_futrue类型允许使用第二次, 并且使用获得结果与第一次一样,如果有一场,抛出的异常也是一样的futrue共享状态在get调用后就解除,下次调用会发生报错。 但是使用shared_future的时候,get方法可调用多次,但是结果是一样的,例如:
3.std::package_task
std::package_task包装了一个可调用对象的包装类,它将函数和future绑定起来,(std::promise是将数据和future绑定起来),以便异步调用。package_task和promise有点类似,promise保存的是一个共享的状态值,package_task保存的是一个函数。(其实感觉书上的这句话并没有表述清楚)
实际上,std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次对象。在std::future上的高一层是std::package_task和std::promise. 它们内部都有future以便访问异步操作结果。
std::promise包装的是一个异步操作,如果需要获取异步操作的返回值,就用std::package_task
std::promise包装的是一个值,如果需要获取异步操作中的某个值,就可以使用std::promise
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- double sum(double x, double y)
- {
- return x + y;
- }
-
-
- int main()
- {
- // 使用package_task获取返回值
- std::packaged_task<double(double, double)> task(sum); // <double(double, double)>函数参数
- // 获取future
- std::future<double> future = task.get_future();
-
- // 需要将任务移动到线程中进行异步操作
- std::thread t1(std::move(task), 2.4, 5.1);
- t1.join();
-
- std::cout << "The sum is " << future.get() << std::endl;
- return 0;
- }
future的wait_for方法是超时等待返回结果,而wait方法知识等待异步操作完成,没有返回值,将上面的方法改写为wait_for方法:
例如:
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- int factorial(int n) // 计算阶乘
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (n == 1)
- return n;
- return n * factorial(n - 1);
- }
-
- void get_result(std::future<int> &future) // 获取结果
- {
- while (future.wait_for(std::chrono::milliseconds(20)) == std::future_status::timeout) /// 等待线程结束
- {
- std::cout << "..";
- }
- std::cout << std::endl;
- std::cout << "The result is " << future.get() << std::endl;
- }
-
- int main()
- {
- // 使用package_task获取返回值
- std::packaged_task<int(int)> task(factorial); // <double(double, double)>函数参数
- // 获取future
- std::future<int> future = task.get_future();
-
- // 需要将任务移动到线程中进行异步操作
- std::thread t1(std::move(task), 7); // 任务处于调用线程中,移动到线程中进行处理
- std::thread t2(get_result, std::ref(future)); // get_result不用move
-
- t1.join();
- t2.join();
-
- //std::cout << "The sum is " << future.get() << std::endl;
- system("pause");
- return 0;
- }
线程异步操作函数async:
std::async可以用来直接创建异步的task,异步任务返回的结果保存在future中,只需要调用future.get()方法就可以获取到返回值。如果不关注异步任务的结果,则可以调用future.wait()方法,等待任务完成。
async的原型是:
std::async(std::launch::async | std::launch::deferred, f, args);
其中:
第一个参数是创建线程的方式:
std::launch::async在调用async时就创建线程。
std::launch::deferred延迟加载方式创建线程,直到调用了future的get或者wait方法时才会创建线程
第二个参数是线程函数
第三个参数是线程函数的参数
基本用法:
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- int factorial(int n) // 计算阶乘
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (n == 1)
- return n;
- return n * factorial(n - 1);
- }
-
-
- void func()
- {
- std::this_thread::sleep_for(std::chrono::seconds(1));
- std::cout << "This function has no operation" << std::endl;
- }
-
-
- int main()
- {
- std::future<int> future1 = std::async(std::launch::deferred, factorial, 5); // 线程函数有返回值
- std::future<void> future2 = std::async(std::launch::async, func); // 线程函数没有参数
-
- future2.wait(); // 调用wait()
- std::cout << "Factorial is " << future1.get() << std::endl; //调用get()
- system("pause");
- return 0;
- }
也可以使用async来创建线程,一般也推荐这样做:
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- void func1()
- {
- for (int i = 0; i < 1000; i++)
- {
- std::cout << "Thread Id is " << std::this_thread::get_id() << std::endl;
- }
- }
-
- int main()
- {
- std::future<void> future1 = std::async(std::launch::async, func1); // 线程函数没有参数
- std::future<void> future2 = std::async(std::launch::async, func1); // 线程函数没有参数
-
- future1.wait();
- future2.wait(); // 调用wait()
-
- system("pause");
- return 0;
- }
最后介绍一下std::shared_future
shared_future的使用与futrue相似,因为futrue共享状态在get调用后就解除,下次调用会发生报错,而shared_futrue类型允许使用第二次, 并且使用获得结果与第一次一样,如果有一场,抛出的异常也是一样的。创建方式如下:
std::shared_futrue f = std::async(task).share();
- // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <iostream>
- #include <future>
- #include <thread>
- #include <chrono>
-
- int func1(int n)
- {
- return n * 10;
- }
-
- int main()
- {
- std::shared_future<int> future = std::async(std::launch::async, func1, 2).share(); // 线程函数没有参数
-
- std::cout << future.get() << std::endl;
- std::cout << future.get() << std::endl; // 第二次调用
-
- system("pause");
- return 0;
- }
应该优先使用async取代线程的创建,它更加方便的实现了异步调用。
-------------------------------------------------------分割线------------------------------------------------------------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。