std::this_thread::sleep_for()
函数,在各次查验之间短期休眠。
std::condition_variable
std::condition_variable
仅限于与 std::mutex
一起使用std::condition_variable_any
std::condition_variable
应予优先采用,除非有必要令程序更灵活
内
std::future<>
)std::shared_future<>
)std::future
和 std::shared_future
。std::experimental
名字空间中给出了上述类模板的扩展版本:std::experimental::future<>
和 std::experimental::shared_future<>
。
std::thread
没有提供直接回传结果的方法,所以函数模板 std::async()
应运而生
中。std::future
取得异步任务的函数返回值int find_the_answer_to_ltuae();
void do_other_stuff();
....
std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
do_other_stuff();
std::cout<<"The answer is "<<the_answer.get()<<std::endl;
std::async()
补充一个参数,以指定采用哪种运行方式, 参数的类型是 std::launch
。
std::launch::deferred
wait()
或 get()
,任务函数才会执行std::launch::async
std::launch::deferred | std::launch:: async
auto f=std::async(std::launch::deferred,func,std::ref(parame));
std::packaged_task<>
连结了 future 对象与函数(或可调用对象)
std::packaged_task<>
对象在执行任务时,会调用关联的函数(或可调用对象),把返回值保存为 future 的内部数据,并令 future 准备就绪std::packaged_task<>
实例之中,再传递给任务调度器或线程池std::packaged_task<>
具有成员函数 get_future()
,它返回 std::future<>
实例std::packaged_task<>
特化示例:template<>
class packaged_task<std::string(std::vector<char>*,int)>
{
public:
template<typename Callable>
explicit packaged_task(Callable&& f);
std::future<std::string> get_future();
void operator()(std::vector<char>*,int);
};
std::packaged_task
对象是可调用对象
std::thread
对象std::packaged_task
实现std::promise
给出了一种异步求值的方法(类型为T)
std::future
对象与结果关联,能延后读出需要求取的值std::promise
和 std::future
可实现下面的工作机制
get_future()
set_value()
若经由 std::async()
调用的函数抛出异常,则会被保存到 future 中
get()
被调用get()
后存储在内的异常即被重新抛出std::promise
也具有同样的功能它通过成员函数的显式调用实现。
set_value()
set_exception()
extern std::promise<double> some_promise;
try
{
some_promise.set_value(calculate_value());
}
catch(...)
{
//std::current_exception() 用于捕获抛出的异常
some_promise.set_exception(std::current_exception());
}
some_promise.set_exception(std::make_exception_ptr(std::logic_error("foo ")))
;另一种方法保存异常到 future 中:
set()
成员函数,也不执行包装的任务,而直接销毁与 future 关联的 std::promise
对象或 std::packaged_task
对象std::future_error
存储为异步任务的状态数据std::future_errc::broken_promise
future自身存在限制
std::shared_future
。std::future
特性
get()
仅能被有效调用唯一一次get()
会进行移动操作,之后该值不复存在只要同步操作是一对一地在线程间传递数据,std::future
就都能处理
std::shared_future
对象的副本,它们为各线程独自所有,并被视作局部变量valid()
,用于判别异步状态是否有效。std::shared_future
,实现并行处理。每个单元格的终值都唯一确定,并可以用于其他单元格的计算公式std::shared_future
std::promise<std::string> p;
std::shared_future<std::string> sf(p.get_future());
std::condition_variable
含有成员函数 wait_for()
和 wait_until()
std::chrono::system_clock::now()
可返回系统时钟的当前时刻time_point
成员类型(member type),它是该时钟类自有的时间点类some_clock::now()
的返回值的类型就是 some_clock::time_point
std::ratio<1,25>
std::ratio<5,2>
is_steady
std::chrono::system_clock
类不是恒稳时钟,因为它可调整。now()
,后来返回的时间值甚至早于前一个std::chrono::steady_clock
std::chrono::system_clock
from_time_t()
和 to_time_t()
std::chrono::high_resolution_clock
std::chrono::duration<>
是标准库中最简单的时间部件
std::chrono::duration>
std::chrono::duration>
std::chrono::duration>
using namespace std::chrono_literals;
auto one_day=24h;
auto half_an_hour=30min;
auto max_time_between_messages=30ms;
std::chrono::duration_cast<>
完成std::chrono::duration<>
实例//等待某个future进入就绪状态,并以35毫秒为限
std::future<int> f=std::async(some_task);
if(f.wait_for(std::chrono::milliseconds(35))==std::future_status::ready)
do_something_with(f.get());
std::future_status::timeout
std::future_status::ready
std::future_status::deferred
std::chrono::time_point<>
的实例表示
time_since_epoch()
std::chrono::high_resolution_clock::now() + std::chrono::nanoseconds(500)
auto start=std::chrono::high_resolution_clock::now();
do_something();
auto stop=std::chrono::high_resolution_clock::now();
std::cout<<"do_something() took "
<(stop-start).count()
<<" seconds"<
std::chrono::system_clock::to_time_point()
转换 time_t 值
some_clock::now()
和前向偏移相加得出时间点std::condition_variable cv;
bool done;
std::mutex m;
bool wait_loop()
{
auto const timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
std::unique_lock<std::mutex> lk(m);
while (!done)
{
if (cv.wait_until(lk, timeout) == std::cv_status::timeout)
break;
}
return done;
}
超时时限的最简单用途是,推迟特定线程的处理过程,若它无所事事,就不会占用其他线程的处理时间
std::this_thread::sleep_for()
std::this_thread::sleep_until()
普通的 std::mutex
和 std::recursive_mutex
不能限时加锁
std::timed_mutex
和 std::recursive_timed_mutex
可以限时加锁
try_lock_for()
和 try_lock_until()
C++ 标准库中接受超时时限的函数
std::this_thread
sleep_for
sleep_unitl
std::condition_variable
或 std::condition_variable_any
wait_for(lock, duration)
wait_until(lock, time_point)
std::cv_status::timeout
或 std::cv_status::no_timeout
wait_for(lock, duration, predicate)
wait_until(lock, time_point, predicate)
std::timed_mutex
, std::recursive_timed_mutex
或 std::shared_timed_mutex
try_lock_for(duration)
try_lock_until(time_point)
std::shared_timed_mutex
try_lock_shared_for(duration)
try_lock_shared_until(time_point)
std::unique_lock
, unique_lock(lockable, duration)
或 unique_lock(lockable, time_point)
try_lock_for(duration)
try_lock_until(time_point)
owns_lock()
返回 true,否则返回 falsestd::shared_lock
, shared_lock(lockable,duration)
或 shared_lock(lockable,time_point)
try_lock_for(duration)
try_lock_until(time_point)
owns_lock()
返回 true,否则返回 falsestd::future
或 std::shared_future
wait_for(duration)
wait_until(time_point)
std::future_status::timeout
std::future_status::ready
std::future_status::deferred
std::async()
)
std::async()
每次都开启新线程,那么只要递归3层,就会有8个线程同时运行spawn_task()
)
std::result_of
被弃用,这个函数需要重写std::async()
退下“火线”,而优先采用线程池模式std::promise
和 std:: packaged_task
的新版本
std::experimental::future
实例,而非 std::future
std::experimental::future<int> find_the_answer;
auto fut=find_the_answer();
auto fut2=fut.then(find_the_question);
assert(!fut.valid());
assert(fut2.valid());
find_the_question()
会在“某一线程上”运行
when_any()
胜任如下情形
std::experimental::when_any()
和 std::experimental::when_all()
的全部使用方式:
std::experimental::future
移动复制到函数中,而且它们都以传值的方式接收参数,所以我们需要显式地向函数内部移动 future,或传递临时变量。std::experimental::latch
由头文件
定
count_down()
,一旦计数器减到 0,它就进入就绪状态wait()
is_ready()
count_down_and_wait()
void foo(){
unsigned const thread_count=...;
//构建线程闩对象done
latch done(thread_count);
my_data data[thread_count];
std::vector<std::future<void> > threads;
for(unsigned i=0;i<thread_count;++i)
//用std::async()发起相同数量的线程
threads.push_back(std::async(std::launch::async,[&,i]{
//各线程负责生成相关的数据块
data[i]=make_data(i);
//在完成时即令计数器减持
done.count_down();
//进行下一步处理
do_more_stuff();
}));
//主线程在处理生成的数据之前,只要在线程闩上等待,就能等到全部数据准备完成
done.wait();
process_data(data,thread_count);
//std::future 的析构函数会发生自动调用,这保证了前面所启动的线程全都会结束运行
}
std::experimental::barrier
std::experimental::flex_barrier
中定义std::experimental::barrier
针对的场景
arrive_and_wait()
等待同步组的其他线程arrive_and_drop()
,即可令线程显式地脱离其同步组std::experimental::flex_barrier
类的接口与 std::experimental::barrier
类的不同之处仅仅在于
内is_lock_free()
,准许使用者判定某一给定类型上的操作是能由原子指令(atomic instruction)直接实现
x.is_lock_free()
返回 truex.is_lock_free()
返回 falsestd::atomic
std::atomic::is_always_lock_free
的值在编译期即确定为 falsestd::atomic
都以无锁结构形式实现std::atomic::is_always_lock_free
的值会是 trueis_lock_free()
成员函数
std::atomic_flag
std::atomic_flag
的对象在初始化时清零test_and_set()
查值并设置成立clear()
清零std::atomic<>
特化得出的std::atomic<>
特化, 而不用别名load()
store()
exchange()
compare_exchange_weak()
compare_exchange_strong()
+=、−=、*=
和 |=
std::atomic<>
特化都支持 ++
和 −−
运算
fetch_add()
和 fetch_or()
等std::atomic<>
并不局限于上述特化类型, 它其实具有泛化模板
load()、store()
exchange()、compare_exchange_weak()、compare_exchange_strong()
std::memory_order
取值
std::memory_order
具有6个可能的值
std::memory_order_relaxed
std::memory_order_release
std::memory_order_seq_cst
std::memory_order_relaxed
std::memory_order_consume
std::memory_order_acquire
std::memory_order_seq_cs
std::memory_order_relaxed
std::memory_order_consume
std::memory_order_acquire
std::memory_order_release
std::memory_order_acq_rel
std::memory_order_seq_cst
std::atomic_flag
是最简单的标准原子类型,表示一个布尔标志。
std::atomic_flag
类型的对象必须由宏 ATOMIC_FLAG_INIT
初始化
std::atomic_flag f=ATOMIC_FLAG_INIT
。std::atomic_flag
std::atomic_flag
对象具有静态存储期,它就会保证以静态方式初始化,从而避免初始化次序的问题。
std::atomic_flag
对象的初始化后,我们只能执行3种操作
clear()
test_and_set()
clear()
和 test_and_set()
指定内存次序clear()
是存储操作,因此无法采用 std::memory_order_acquire
或std::memory_order_acq_rel
内存次序test_and_set()
是“读-改-写”操作,因此能采用任何内存次序//调用显式地采用释放语义将标志清零
f.clear(std::memory_order_release);
//调用采用默认的内存次序,获取旧值并设置标志成立
bool x=f.test_and_set();
std::atomic_flag
对象拷贝构造出另一个对象std::atomic_flag
功能有限,所以它可以完美扩展成自旋锁互斥(spin-lock mutex)class spinlock_mutex
{
std::atomic_flag flag;
public:
spinlock_mutex():
flag(ATOMIC_FLAG_INIT)
{}
void lock()
{
while(flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
lock()
函数内忙等。
std::atomic_flag
,它是一个功能更齐全的布尔标志store()
是存储操作load()
是载入操作exchange()
是“读-改-写” 操作std::atomic<bool> b;
bool x=b.load(std::memory_order_acquire);
b.store(true);
x=b.exchange(false,std::memory_order_acq_rel);
compare_exchange_weak()
和 compare_exchange_strong()
bool expected=false;
extern atomic<bool> b; //由其他源文件的代码设定变量的值
//只要expected变量还是false,
//就说明compare_exchange_weak()的调用发生佯败,我们就继续循环
while(!b.compare_exchange_weak(expected,true) && !expected);
compare_exchange_strong()
才返回 falsecompare_exchange_weak()
可能导致佯败,但改用 compare_exchange_strong()
却会形成双重嵌套循环(因 compare_exchange_strong()
自身内部含有一个循环),那么采用compare_exchange_weak()
比较有利于性能。compare_exchange_strong()
则更加合理。因为只要预期值没有变化,就可避免重复计算std::memory_order_acq_rel
内存次序std::memory_order_relaxed
内存次序std::atomic
和 std::atomic_flag
的另一个不同点是
is_lock_free()
,检查 std::atomic
是否具备真正的无锁操作std::atomic
相似,同样不能拷贝复制或拷贝赋值std::atomic
也具备成员函数
is_lock_free()
load()
store()
exchange()
compare_exchange_weak()
compare_exchange_strong()
T*
类型std::atomic
提供的新操作是算术形式的指针运算
fetch_add()
fetch_sub()
+=
−=
++
−−
std::atomic
,其指向 Foo 对象数组中的第一项,那么操作 x+=3
会使之变为指向数组第四项,并返回 Foo*
型的普通指针class Foo{};
Foo some_array[5];
std::atomic<Foo*> p(some_array);
//接收附加的参数
//p.fetch_add(3,std::memory_order_release);
Foo* x=p.fetch_add(2); //令p加2,返回旧值
assert(x==some_array);
assert(p.load()==&some_array[2]);
x=(p-=1); // 令p减1,返回新值
assert(x==&some_array[1]);
assert(p.load()==&some_array[1]);
std::atomic
和 std::atomic
这样的整数原子类型上,可以执行的操作颇为齐全
load()
store()
exchange()
compare_exchange_weak()
compare_exchange_strong()
fetch_add()
fetch_sub()
fetch_and()
fetch_or()
fetch_xor()
+=
、−=
、&=
、|=
和 ^=
++x
、x++
、−−x
和 x−−
std::atomic<>
memcpy()
或采取与之等效的行为完成它std::atomic<>
生成无锁代码
memcpy()
和 memcmp()
的适用条件
std::atomic
和 std::atomic
可以为我们所用
compare_exchange_strong()
函数,其行为可能出人意料int
或 void*
的体积,只要用户自定义的 UDT 类型的体积不超过它们,那么在大多数常见的硬件平台上,都可以为 std::atomic
类型采用原子指令std::atomic>
vector
含有非平实拷贝构造函数和非平实拷贝赋值操作符std::atomic_load()
std::atomic_flag
的非成员函数是
std::atomic_flag_test_and_set()
std::atomic_flag_clear()
std::atomic_flag_test_and_set_explicit()
std::atomic_flag_clear_explicit()
std::shared_ptr<my_data> p;
void process_global_data()
{
std::shared_ptr<my_data> local=std::atomic_load(&p);
process_data(local);
}
void update_global_data()
{
std::shared_ptr<my_data> local(new my_data);
std::atomic_store(&p,local);
}
std::share_ptr
的函数也具有变体std::experimental::atomic_shared_ptr
,其也是一种原子类型。我们须包含头文件
才能使用该类型
atomic_shared_ptr<>
被设计成一个独立类型,
std::shared_ptr
对象,它没有增加额外开销is_lock_free()
判定std::share_ptr
类型
std::experimental::atomic_shared_ptr
应予优先采用(就算它不是无锁实现)
std::mutex
和 std::future<>
,都以这种强制服从的内存次序为基础data_ready
的操作提供了所需的强制次序,它属于 std::atomic
类型#include
#include
#include
std::vector<int> data;
std::atomic<bool> data_ready(false);
void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::chrono::milliseconds(1));
}
std::cout<<"The answer="<<data[0]<<"\n";
}
void writer_thread()
{
data.push_back(42);
data_ready=true;
}
memory_order_consume
memory_order_consume
标记,因此实际上这一区别对我们影响甚微。fetch_add()
和 exchange()