前言
本文是记录个人对c++11新特性的学习,文中所用可能会采用其他大神的图、思路、例子等,请大神们见谅。本博客文章是在学习过程的一些总结,整理出来,分享给大家,希望对各位读者有帮助,文章中的总结可能存在很多不完整或有错误的地方,也希望读者指出。
1、std::thread
c++11中,可以通过创建std::thread类的对象来创建其他线程,每个std :: thread对象都可以与一个线程相关联,头文件< thread>
使用std :: thread对象附加一个回调。 这些回调可以为函数指针、函数对象、Lambda函数
通过std::thread thObj(< CALLBACK>)来创建
#include <iostream>
#include <thread>
void thread_function() {
for (int i = 0; i < 5; i++)
std::cout << "thread function excuting" << std::endl;
}
class DisplayThread {
public:
void operator ()() {
for (int i = 0; i < 100; i++)
std::cout << "Display Thread Excecuting" << std::endl;
}
};
int main() {
std::thread threadObj(thread_function); //调用函数指针
std::thread threadObj((DisplayThread())); //调用函数对象
for (int i = 0; i < 5; i++)
std::cout << "Display from MainThread" << std::endl;
threadObj.join();
std::cout << "Exit of Main function" << std::endl;
return 0;
}
std::thread::get_id() —成员函数中给出对应线程对象的id;
std::thread::hardware_concurrency()–获取CPU核心数量
std::this_thread::sleep_for(std::chrono::second(3))–让当前线程休眠一定时间,例如当前为休眠3秒
join():调用join()函数等待这个线程执行完毕
detach():调用detach()后,std::thread对象不再与实际执行线程相关联
1.1、线程创建方式
通过std::bind或者lambda表达式创建线程
void func(int a, double b)
{
}
int main()
{
std::thread t1(std::bind(fun, 1, 2));
std::thread t2([](int a, double b){}, 1, 2);
t1.join();
t2.join();
std::thread t3(func, 1, 2); //这里直接创建线程而使用join或者detach,会导致func函数未执行完,std::thread生命周期就结束,引发异常。
return 0;
}
std::vector<std::thread> g_lsit;
std::vector<std::shared_ptr<std::thread>> g_lsit2;
void createThread()
{
std::thread t(func);
g_list.push_back(std::move(t));
g_list2.push_back(std::make_shared<std::thread>(func));
}
int main()
{
createThread();
for(auto& thread : g_list)
{
thread.join();
}
for(auto& thread : g_list2)
{
thread->join();
}
return 0;
}
注意点:
1)线程可以通过std::move函数进行移动,但不能复制,线程被移动后,线程对象不再代表任何线程;
2)std::thread出了作用域之后将会析构,如果线程函数还未执行完则会发生错误,所以 需要保证线程函数的生命周期在std::thread的生命周期内,通过join或者detach函数保证,或者将线程对象保存到容器中来保证
1.2、线程传递参数
要将参数传递给线程的可关联对象或函数,只需将参数传递给std::thread构造函数,默认情况下,所有的参数都将复制到新线程的内部存储中
#include <iostream>
#include <string>
#include <thread>
void threadCallback(int x, std::string str) {
std::cout << "Passed Number = " << x << std::endl;
std::cout << "Passed String = " << str << std::endl;
}
void threadCallback(int const& x) {
int& y = const_cast<int&>(x);
y++;
std::cout << "Inside Thread x = " << x << std::endl;
}
class DummyClass {
public:
DummyClass() { }
DummyClass(const DummyClass& obj) { }
void sampleMemberfunction(int x) {
std::cout << "Inside sampleMemberfunction " << x << std::endl;
}
};
int main() {
int x = 10;
std::string str = "Sample String";
std::thread threadObj(threadCallback, x, str); //值传递参数方式
std::thread threadObj(threadCallback, x); //引用传递参数方式,引用方式传递到线程值是x的引用复制值,所以main内的x是不会因为threadCallback的内部参数改变而改变
std::thread threadObj(threadCallback, std::ref(x));//使用std::ref可进行修改,threadCallback内部修改的X值,在main中也同样被修改。
DummyClass dummyObj;
int x = 10;
std::thread threadObj(&DummyClass::sampleMemberfunction, &dummyObj, x); //指定一个类的成员函数的指针作为线程函数,将指针传递给成员函数作为回调函数,并将指针指向对象作为第二个参数
threadObj.join();
return 0;
}
std::ref 用于包装按引用传递的值,函数内部的修改影响外面。
std::cref 用于包装按const引用传递的值,const引用传递,函数内部不能修改。
join和detach
join():等待线程完成函数,主线程需要等待子线程运行结束了才可以结束
detach():分离线程函数,使用detach()函数会让线程在后台运行;如果线程与主线程之间共享变量或者引用,则要慎用分离,防止出现主线程先结束,销毁线程对象及局部变量,引发未知错误
注:同一个线程不能同时调用jion()和detach()
joinable():判断当前线程是否可连接,返回true为可连接
线程join和detach都不调用
//std::thread的构造函数如下
class thread {
private:
id _M_id;
public:
thread() noexcept = default;
template<typename _Callable,
typename... _Args,
typename = _Require<__not_same<_Callable>>>
explicit thread(_Callable&& __f, _Args&&... __args) {
//...
}
~thread() {
if (joinable())
std::terminate();
}
// 禁止复制
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
// std::thread 只具有移动属性
thread(thread&& __t) noexcept
{ swap(__t); }
thread& operator=(thread&& __t) noexcept {
if (joinable())
std::terminate();
swap(__t);
return *this;
}
//...
}
从上可知:如果线程不调用join和detach,线程的状态将是joinable,在析构时直接调用terminate函数,程序将直接退出;
线程内调用join
线程内调用自己jion自己,是直接报错误的,必须是其他线程调用join
2、互斥量
用于线程同步、保护多线程访问的共享数据
1)std::mutex–独占的互斥量,不能递归使用
2)std::timed_mutex–带超时的互斥量,不能递归使用
3)std::recursive_mutex–递归互斥量,不带超时功能
4)std::recursive_timed_mutex–带超时的递归互斥量
2.1、mutex和lock_guard
互斥量包装程序,它提供了一种方便的RAII(Resource acquisition is initialization )风格的机制来在作用域块的持续时间内拥有一个互斥量;
特点:
1)创建即加锁,作用域结束自动析构并解锁,无需手工解锁
2)不能中途解锁,必须等作用域结束才解锁
3)不能复制
std::mutex g_lock;
void func()
{
std::lock_guard<std::mutex> locker(g_lock); //出作用域自动解锁
....
}
std::unique_lock详解
unique_lock是个类模板,unique_lock比lock_guard灵活很多,效率上差一点,内存占用多一点
它允许延迟锁定,限时深度锁定,递归锁定,锁定所有权的转移以及与条件变量一起使用。
特点如下:
1)创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
2)可以随时加锁解锁
3)作用域规则同 lock_grard,析构时自动释放锁
4)不可复制,可移动(移动语义)
5)条件变量需要该类型的锁作为参数(此时必须使用unique_lock)
std::mutex m;
std::unique_lock<std::mutex> lock2(m, std::defer_lock); //只是单单的用m初始化lock,但是不进行加锁
std::unique_lock<std::mutex> lock(m, std::chrono::seconds(1)); //在1秒钟之后,对m进行加锁,如果加锁成功就返回,加锁失败就阻塞等待
std::unique_lock<std::mutex> lock(m, std::try_to_lock);//尝试加锁m,但是不阻塞
std::try_to_lock():会尝试加锁,使用前不能自己lock()
std::defer_lock():可以不给mutex加锁,相当于初始化了一个没有加锁的mutex,前提是先不能lock().
std::try_lock():尝试给锁上锁,如果上锁失败,返回false,否则返回true
std::lock():允许你一次锁定多个mutex,会锁住它收到的所有mutex,并且阻塞到所有的mutex都被锁定或直到抛出异常才返回
std::release():返回它管理的mutex对象指针,并释放所有权;如果你release之前,已经加锁了,release之后你要负责unlock()
unique_lock所有权的传递
std::unique_lock<std::mutex> sbguard(my_mutex);//所有权概念
//sbguard拥有my_mutex的所有权;sbguard可以把自己对mutex(my_mutex)的所有权转移给其他的unique_lock对象;所以unique_lock对象这个mutex的所有权是可以转移,但是不能复制。
std::unique_lock<std::mutex> sbguard1(my_mutex);
std::unique_lock<std::mutex> sbguard2(sbguard1);//此句是非法的,复制所有权是非法的
//方法一:通过std::move实现所有权传递
std::unique_lock<std::mutex> sbguard2(std::move(sbguard));//移动语义,现在先当与sbguard2与my_mutex绑定到一起了
//现在sbguard1指向空,sbguard2指向了my_mutex
//方法二:
std::unique_lock<std::mutex> rtn_unique_lock()
{
std::unique_lock<std::mutex> tmpguard(my_mutex);
return tmpguard;//从函数中返回一个局部的unique_lock对象是可以的。三章十四节讲解过移动构造函数。
//返回这种举报对象tmpguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
}
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1 = rtn_unique_lock();
msgRecvQueue.push_back(i);
}
}
2.2、recursive_mutex
允许同一个线程多次获得该互斥锁,解决同一个线程多次获取互斥量的死锁问题
class X {
std::recursive_mutex m;
std::string shared;
public:
void fun1() {
std::lock_guard<std::recursive_mutex> lk(m);
shared = "fun1";
std::cout << "in fun1, shared variable is now " << shared << '\n';
}
void fun2() {
std::lock_guard<std::recursive_mutex> lk(m);
shared = "fun2";
std::cout << "in fun2, shared variable is now " << shared << '\n';
fun1(); // ① 递归锁在此处变得有用
std::cout << "back in fun2, shared variable is " << shared << '\n';
};
};
int main()
{
X x;
std::thread t1(&X::fun1, &x);
std::thread t2(&X::fun2, &x);
t1.join();
t2.join();
}
注意点:
1)允许递归互斥容易放纵复杂逻辑的产生,导致一些多线程同步引起的问题,而这部分逻辑往往是可以优化处理的
2)递归锁的效率比非递归锁的要低
3)递归锁虽允许同一个线程多次获取同一个互斥锁,但由一定次数限制,一旦超过则进行调用的时候会抛出std::system错误
2.3、recursive_timed_mutex和timed_mutex
timed_mutex是超时独占锁;
recursive_timed_mutex:带超时的递归锁
std::try_lock_for();是等待一段时间,参数是时间段
std::try_lock_until();参数是一个时间点,到那个时间点之前等待获取锁
3、条件变量
同步等待机制、阻塞单个或多个线程,直到另外一个线程发通知或者超时,唤醒当前阻塞线程。需要和互斥量一起使用
condition_variable:配合unique_lock使用进行wait操作
condition_variable_any:和任意锁配合使用,比较灵活,但效率比condition_variable低
函数:
notify_one-- 通知一个等待线程
notify_all-- 通知所有等待线程
wait – 阻塞当前线程,直到条件变量被唤醒,
wait_for – 阻塞当前线程,直到条件变量被唤醒或到指定时间时长
wait_until – 阻塞当前线程,直到条件变量被唤醒或到指定时间点
过程简述:
1)一个线程因等待"条件变量的条件成立"而挂起;(condition_variable类成员wait 、wait_for 或 wait_until)
2)另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程(使用的是condition_variable类成员notify_one或者notify_all函数)
注意点:
实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒
while (!(xxx条件) )
{
//虚假唤醒发生,由于while循环,再次检查条件是否满足,
//否则继续等待,解决虚假唤醒
wait();
}
4、原子量
std::atomic,使用了原子量就不使用互斥量
template < class T > struct atomic {
//判断atomic<T>中的T对象是否为lock_free
bool is_lock_free() const volatile;
bool is_lock_free() const;
//基本的构造函数,重载函数
atomic() = default;
constexpr atomic(T val);
atomic(const atomic &) = delete;
atomic & operator=(const atomic &) = delete;
atomic & operator=(const atomic &) volatile = delete;
T operator=(T val) volatile;
T operator=(T val);
operator T() const volatile;
operator T() const;
//一系列封装的原子操作
T exchange(T val, memory_order = memory_order_seq_cst) volatile;
T exchange(T val, memory_order = memory_order_seq_cst);
void store(T val, memory_order = memory_order_seq_cst) volatile;
void store(T val, memory_order = memory_order_seq_cst);
T load(memory_order = memory_order_seq_cst) const volatile;
T load(memory_order = memory_order_seq_cst) const;
bool compare_exchange_weak(T& expected, T val, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst);
bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst);
};
使用举例:
#include <iostream>
#include <atomic>
#include <vector>
#include <functional>
#include <thread>
using namespace std;
std::atomic<long> sum;
const int count = 100000;
void add()
{
//每个线程来循环对sum实行加1操作
for (int j = 0; j < count; ++j)
sum++;
}
int main()
{
std::vector<std::thread> threads;
//开始计时
clock_t start = clock();
for (int j = 0; j < 5; ++j)
threads.push_back(std::move(std::thread(add)));
for (auto & t : threads)
t.join();
//结束计时
clock_t finish = clock();
cout<<"result:"<<sum<<endl;
cout<<"time:"<<finish -start<<"ms"<<endl;
return 0;
}
//输出结果:result:50000,time:31290ms
内存序
bool compare_exchange_weak(T& expected, T val, memory_order = memory_order_seq_cst) volatile;
//6种类型的内存序, 用于控制变量在不同线程间的顺序可见性
typedef enum memory_order {
memory_order_relaxed, //不对执行顺序做保证,编译器和处理器可以随意优化
memory_order_consume, //防止在其后对原子变量有依赖的操作被重排到前面去
memory_order_acquire, //要求当前调用之后的读写操作不会重排到前面来
memory_order_release, //要求当前调用之后的读写操作不会重排到前面来
memory_order_acq_rel, //读取-修改-写回
memory_order_seq_cst //默认的内存序
} memory_order;
5、call_once和once_flag的使用
保证函数在多线程的环境中只被调用一次
std::once_flag flag;
void do_once()
{
std::call_one(flag, [](){std::cout<<"Called once"<<std::endl;});
}
int main(void)
{
std::thread t1(do_once);
std::thread t2(do_once);
std::thread t3(do_once);
t1.join();
t2.join();
t3.join();
//输出:Called once
}
注意点:once_flag的生命周期,它必须要比使用它的线程的生命周期要长。所以通常定义成全局变量比较好
//完美的解决线程安全的单例模式
#include <iostream>
#include <memory>
#include <mutex>
class Singleton {
public:
static Singleton& GetInstance() {
static std::once_flag s_flag;
std::call_once(s_flag, [&]() {
instance_.reset(new Singleton);
});
return *instance_;
}
~Singleton() = default;
void PrintAddress() const {
std::cout << this << std::endl;
}
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
static std::unique_ptr<Singleton> instance_;
};
std::unique_ptr<Singleton> Singleton::instance_;
int main() {
Singleton& s1 = Singleton::GetInstance();
s1.PrintAddress();
Singleton& s2 = Singleton::GetInstance();
s2.PrintAddress();
return 0;
}
6、异步操作
让调用方法的主线程不需要同步等待调用函数,从而可以让主线程继续执行它下面的代码(回调函数)
当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。
C++11中的异步操作主要有std::future、std::async、std::promise、std::packaged_task
6.1、std::future
future对象是std::async、std::promise、std::packaged_task的底层对象,用来传递其他线程中操作的数据结果
#include <future>
template< class T > class future;
template< class T > class future<T&>;
template<> class future<void>;
成员函数:
share – 从 *this 转移共享状态给 shared_future 并返回它
get – 返回结果
valid – 检查 future 是否拥有共享状态
wait – 等待结果变得可用
wait_for – 等待结果,如果在指定的超时间隔后仍然无法得到结果,则返回
wait_until – 等待结果,如果在已经到达指定的时间点时仍然无法得到结果,则返回
异步访问机制:
类模板std::future提供访问异步操作结果的机制:
1)通过 std::promise、std::packaged_task和std::async创建的异步操作能提供一个 std::future 对象给该异步操作的创建者。
2)异步操作的创建者能用各种方法查询、等待或从 std::future 提取值。若异步操作仍未提供值,则这些方法可能阻塞。
3)异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的 std::future 的共享状态(例如 std::promise::set_value )进行
注意点:
1)std::future 所引用的共享状态不与另一异步返回对象共享(与 std::shared_future 相反)
2)std::asyanc是std::future的高级封装, 一般我们不会直接使用std::futrue,而是使用对std::future的高级封装std::async
#include <iostream>
#include <future>
#include <thread>
int main()
{
// 来自 packaged_task 的 future
std::packaged_task<int()> task([](){ return 7; }); // 包装函数
std::future<int> f1 = task.get_future(); // 获取 future
std::thread(std::move(task)).detach(); // 在线程上运行
// 来自 async() 的 future
std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });
// 来自 promise 的 future
std::promise<int> p;
std::future<int> f3 = p.get_future();
std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach();
std::cout << "Waiting..." << std::flush;
f1.wait();
f2.wait();
f3.wait();
std::cout << "Done!\nResults are: "<< f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
system("pause");
return 0;
}
//输出
//Waiting...Done!
//Results are: 7 8 9
6.2、std::promise
std::promise提供了不同线程之间的数据同步机制,可存储一个某种类型的值,并将其传递给对应的future,即使这个future不在同一个线程中也可以访问到这个值
注意点:取值是间接的通过promise内部提供的future来获取的
#include<future>
template< class R > class promise;
template< class R > class promise<R&>;
template<> class promise<void>;
应用场景:
- 空模板
- 非 void 特化,用于在线程间交流对象
- void 特化,用于交流无状态事件
promise 可以对共享状态做三件事:
1)使就绪: promise 存储结果或异常于共享状态。标记共享状态为就绪,并解除阻塞任何等待于与该共享状态关联的 future 上的线程。
2)释放: promise 放弃其对共享状态的引用。若这是最后一个这种引用,则销毁共享状态。除非这是 std::async 所创建的未就绪的共享状态,否则此操作不阻塞。
3)抛弃: promise 存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 类型异常,令共享状态为就绪,然后释放它。
成员函数:
swap – 交换二个 promise 对象
get_future – 返回与承诺结果关联的future
set_value – 设置结果为指定值
set_value_at_thread_exit – 设置结果为指定值,同时仅在线程退出时分发提醒
set_exception – 设置结果为指示异常
set_exception_at_thread_exit – 设置结果为指示异常,同时仅在线程退出时分发提醒
c++11系统函数:
std::swap(std::promise)-- 特化 std::swap 算法(函数模板)
std::uses_allocatorstd::promise(C++11) – 特化 std::uses_allocator 类型特征 (类模板特化)
#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
void accumulate(std::vector<int>::iterator first,
std::vector<int>::iterator last,
std::promise<int> accumulate_promise)
{
int sum = std::accumulate(first, last, 0);
accumulate_promise.set_value(sum); // 提醒 future
}
void do_work(std::promise<void> barrier)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
barrier.set_value();
}
int main()
{
// 演示用 promise<int> 在线程间传递结果。
std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
std::promise<int> accumulate_promise;
std::future<int> accumulate_future = accumulate_promise.get_future(); // 传递其他线程中操作结果
std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
std::move(accumulate_promise));
accumulate_future.wait(); // 等待结果
std::cout << "result=" << accumulate_future.get() << '\n';
work_thread.join(); // 阻塞等待线程完成
// 演示用 promise<void> 在线程间对状态发信号
std::promise<void> barrier;
std::future<void> barrier_future = barrier.get_future();
std::thread new_work_thread(do_work, std::move(barrier));
barrier_future.wait(); // 等待结果
new_work_thread.join(); // 阻塞等待线程完成
system("pause");
return 0;
}
//输出:result=21
6.3、std::packaged_task
std::packaged_task的作用是为可调用的对象(函数,lambda表达式,bind表达式或者其他表达式)提供一个不同线程之间的数据同步机制,它可以存储一个函数操作,并将其返回值传递给对应的future,而这个future在另一个线程中也可以安全的访问这个值
#include<future>
template< class > class packaged_task; // 不定义
template< class R, class ...Args >
class packaged_task<R(Args...)>;
成员函数:
valid – 检查任务对象是否拥有合法函数
swap – 交换二个任务对象
get_future – 返回与承诺的结果关联的 std::future
operator() – 执行函数
make_ready_at_thread_exit – 执行函数,并确保结果仅在一旦当前线程退出时就绪
reset – 重置状态,抛弃任何先前执行的存储结果
c++11系统函数:
std::swap(std::packaged_task)(C++11) – 特化 std::swap 算法(函数模板)
std::uses_allocatorstd::packaged_task(C++11)(C++17 前) – 特化 std::uses_allocator 类型特征 (类模板特化)
#include <iostream>
#include <cmath>
#include <thread>
#include <future>
#include <functional>
// 避免对 std::pow 重载集消歧义的独有函数
int f(int x, int y) { return std::pow(x, y); }
void task_lambda()
{
std::packaged_task<int(int, int)> task([](int a, int b) {
return std::pow(a, b);
});
std::future<int> result = task.get_future();
task(2, 9);
std::cout << "task_lambda:\t" << result.get() << '\n';
}
void task_bind()
{
std::packaged_task<int()> task(std::bind(f, 2, 11));
std::future<int> result = task.get_future();
task();
std::cout << "task_bind:\t" << result.get() << '\n';
}
void task_thread()
{
std::packaged_task<int(int, int)> task(f);
std::future<int> result = task.get_future();
std::thread task_td(std::move(task), 2, 10);
task_td.join();
std::cout << "task_thread:\t" << result.get() << '\n';
}
int main()
{
task_lambda();
task_bind();
task_thread();
system("pause");
return 0;
}
//输出
//task_lambda: 512
//task_bind: 2048
//task_thread: 1024
6.4、std::async
std::async用于创建异步任务,实际上就是创建一个线程执行相应任务
std::async就是异步编程的高级封装,封装了std::future的操作,基本上可以代替std::thread 的所有事情
std::async的操作,其实相当于封装了std::promise、std::packaged_task加上std::thread。
#include<future>
template< class Function, class... Args >
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
async( std::launch policy, Function&& f, Args&&... args );
参数:
policy:线程的创建策略:一种是调用即创建线程(std::launch::async);另一种是延迟加载方式创建线程(std::launch::deferred),调用时不创建线程,直到调用future的get或者wait时才创建线程。
f:线程函数
args:线程函数的参数
返回值:
指代此次调用 std::async 所创建的共享状态的 std::future
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>
std::mutex m; // 互斥量
struct X {
void foo(int i, const std::string& str) {
std::lock_guard<std::mutex> lk(m);
std::cout << str << ' ' << i << '\n';
}
void bar(const std::string& str) {
std::lock_guard<std::mutex> lk(m);
std::cout << str << '\n';
}
int operator()(int i) {
std::lock_guard<std::mutex> lk(m);
std::cout << i << '\n';
return i + 10;
}
};
template <typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
auto len = end - beg;
if (len < 1000)
return std::accumulate(beg, end, 0);
RandomIt mid = beg + len / 2;
auto handle = std::async(std::launch::async,parallel_sum<RandomIt>, mid, end);
int sum = parallel_sum(beg, mid);
return sum + handle.get();
}
int main()
{
std::vector<int> v(10000, 1);
std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
X x;
// 以默认策略调用 x.foo(42, "Hello") :
// 可能同时打印 "Hello 42" 或延迟执行
auto a1 = std::async(&X::foo, &x, 42, "Hello");
// 以 deferred 策略调用 x.bar("world!")
// 调用 a2.get() 或 a2.wait() 时打印 "world!"
auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
// 以 async 策略调用 X()(43) :
// 同时打印 "43"
auto a3 = std::async(std::launch::async, X(), 43);
a2.wait(); // 打印 "world!"
std::cout << a3.get() << '\n'; // 打印 "53"
system("pause");
return 0;
} // 若 a1 在此点未完成,则 a1 的析构函数在此打印 "Hello 42"
//输出
//The sum is 10000
//Hello 42
//world!
//43
//53
std::launch::async,表示立即开启异步求值
std::launch::deferred,延迟开启,只有当返回的future实例调用get函数时才开启异步求值
而默认情况下的异步属性是std::launch::async | std::launch::deferred,所以到底是立即开启还是延迟开启取决于编译器的不同。如果异步至关重要的话记得在构造函数中指定std::launch::asyn
注意点:std::async替代线程的创建,作为异步操作的首选
6.5、总结
future提供访问异步操作结果的机制,与线程是同一级别
packaged_task和promise将future用作异步操作或者异步结果的连接通道,